diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index c271ee9..fa2df46 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -11,10 +11,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: maven - name: Build and Test with Maven diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml index 5c0cf3a..9b1f409 100644 --- a/.github/workflows/build_deploy.yml +++ b/.github/workflows/build_deploy.yml @@ -18,7 +18,7 @@ jobs: uses: actions/setup-java@v3 with: # configure settings.xml distribution: 'temurin' - java-version: '11' + java-version: '17' server-id: ossrh server-username: OSSRH_USER server-password: OSSRH_PASSWORD diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..9f9c7bd --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,69 @@ +name: "CodeQL" + + on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + # schedule: + # - cron: '16 15 * * 1' + + jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + # Analyzes Java code directly from the codebase without a build + - language: java-kotlin + build-mode: none # analyzes Java only + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Setup java 17. + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" + + diff --git a/.github/workflows/coverage-comment-pr.yml b/.github/workflows/coverage-comment-pr.yml new file mode 100644 index 0000000..e69d351 --- /dev/null +++ b/.github/workflows/coverage-comment-pr.yml @@ -0,0 +1,55 @@ +name: Add coverage comment to PR + +on: + workflow_run: + workflows: ["Java Test and Coverage"] + types: + - completed + +jobs: + add-coverage-comment: + runs-on: ubuntu-latest + permissions: + pull-requests: write + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' + steps: + - name: 'Download artifact' + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id }}, + }); + var matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "pr-comment" + })[0]; + var download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + var fs = require('fs'); + fs.writeFileSync('${{github.workspace}}/pr-comment.zip', Buffer.from(download.data)); + - run: unzip pr-comment.zip + + - name: 'Comment on PR' + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + var fs = require('fs'); + + const issue_number = Number(fs.readFileSync('./pr-number.txt')); + const body = fs.readFileSync('./body.txt', { encoding: 'utf8', flag: 'r' }); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue_number, + body: body + }); diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..5f9e1d2 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,78 @@ +name: Java Test and Coverage + +on: + pull_request: + branches: + - main + +jobs: + test-and-coverage: + name: Test with coverage + runs-on: ubuntu-latest + + steps: + - run: | + git config --global user.name 'eclipse-uprotocol-bot' + git config --global user.email 'uprotocol-bot@eclipse.org' + + - name: Checkout code + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + + - name: Set up Apache Maven Central + uses: actions/setup-java@v3 + with: # configure settings.xml + distribution: 'temurin' + java-version: '17' + server-id: ossrh + server-username: OSSRH_USER + server-password: OSSRH_PASSWORD + gpg-private-key: ${{ secrets.ORG_GPG_PRIVATE_KEY }} + gpg-passphrase: GPG_PASSPHRASE + + + - name: Run tests with coverage + run: | + mvn clean test jacoco:report + + + - name: Extract JaCoCo report + run: | + echo "Extracting coverage percentage from JaCoCo report" + INDEX_PATH="target/site/jacoco/index.html" + export COVERAGE_PERCENTAGE=$(grep -oP '(?<=).*?(?=%)' $INDEX_PATH | sed 's/ //g') + export COVERAGE_PERCENTAGE=$(printf "%.2f" "$COVERAGE_PERCENTAGE") + echo "COVERAGE_PERCENTAGE= $COVERAGE_PERCENTAGE" >> $GITHUB_ENV + echo "COVERAGE_PERCENTAGE: $COVERAGE_PERCENTAGE" + + + - name: Upload JaCoCo Coverage report + uses: actions/upload-artifact@v3 + with: + name: coverage-report + path: target/site/jacoco + + - name: Generate coverage comment + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const COVERAGE_PERCENTAGE = `${{ env.COVERAGE_PERCENTAGE }}`; + const COVERAGE_REPORT_PATH = `https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/`; + + fs.mkdirSync('./pr-comment', { recursive: true }); + + var pr_number = `${{ github.event.number }}`; + var body = ` + Code coverage report is ready! :chart_with_upwards_trend: + + - **Code Coverage Percentage:** ${COVERAGE_PERCENTAGE}% + - **Code Coverage Report:** [View Coverage Report](${COVERAGE_REPORT_PATH}) + `; + + fs.writeFileSync('./pr-comment/pr-number.txt', pr_number); + fs.writeFileSync('./pr-comment/body.txt', body); + + - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + with: + name: pr-comment + path: pr-comment/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b0b8572..a3f865c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: uses: actions/setup-java@v3 with: # configure settings.xml distribution: 'temurin' - java-version: '11' + java-version: '17' server-id: ossrh server-username: OSSRH_USER server-password: OSSRH_PASSWORD diff --git a/NOTICE.adoc b/NOTICE.adoc index e7d6c4c..a1f706f 100644 --- a/NOTICE.adoc +++ b/NOTICE.adoc @@ -27,7 +27,6 @@ SPDX-License-Identifier: Apache-2.0 The following are libraries that uProtocol project uses: -* http://cloudevents.io * http://protobuf.dev NOTE: Please refer to link:pom.xml[] for more information of library dependencies \ No newline at end of file diff --git a/README.adoc b/README.adoc index 719ec44..ea09481 100644 --- a/README.adoc +++ b/README.adoc @@ -24,45 +24,24 @@ To pull the Library from maven central, setting ${uprotocol.version} to the late === Using The Library -.Package Folders -[#pkg-folders,width=100%,cols="20%,80%",options="header"] -|=== - -| Folder | Purpose - -| `*builder*` or `*factory*` -| Contains factory methods for creating uProtocol data types - -| `*serializer*` -| Contains serializers to convert the objects into byte or string form representation of said object - -| `*validator*` -| Contains validators to validate the data types and report errors if the objects are missing or incorrect - -|=== - - .SDK Packages [#sdk-packages,width=100%,cols="20%,80%",options="header"] |=== | Package | Purpose +| xref:src/main/kotlin/org/eclipse/uprotocol/communication/README.adoc[`*communication*`] +| Top level client-facing https://github.com/eclipse-uprotocol/up-spec/tree/main/up-l2[communication layer (uP-L2)] interfaces that applications and services use to implement the publisher/subscriber, notification, and RPC patterns on top of the transport layer (uTransport) API. + | link:src/main/kotlin/org/eclipse/uprotocol/uri/README.adoc[`*uuri*`] -| Uniform Resource Identifier (RFC3986), how uProtocol addresses things (devices, software, methods, topics, etc...) on the network +| Builders, validators, and serializers for uProtocol addressing scheme (UUri). | link:src/main/kotlin/org/eclipse/uprotocol/uuid/README.adoc[`*uuid*`] | Identifier used to uniquely identify (and timestamp) messages that are sent -| link:src/main/java/org/eclipse/uprotocol/transport/README.adoc[`*transport*`] -| Interface declaration used for bidirectional point-2-point communication between uEs. This interface is then implemented by https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/upclient.adoc[uPClient] libraries for a given underlining transport (ex. Binder, MQTT, Zenoh, SOME/IP, DDS, HTTP, etc…​) - -| link:src/main/kotlin/org/eclipse/uprotocol/cloudevent/README.adoc[`*cloudevent*`] -| Common way to represent uProtocol messages using CloudEvent data model used by some transports (ex. MQTT, HTTP, etc…​) - -| link:src/main/java/org/eclipse/uprotocol/rpc/README.adoc[`*rpc*`] -| RPC client and server-side interfaces that are to be implemented by uLink libraries +| link:src/main/kotlin/org/eclipse/uprotocol/transport/README.adoc[`*transport*`] +| https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/up-l1/README.adoc[uP-L1 Transport Layer] interface and data model that wraps communication middlewares like zenoh, mqtt, http, etc... into a thin and simple to use transport interface. This model is used by the communication layer (uP-L2) to send and receive messages and transports are expected to implement the link:src/main/kotlin/org/eclipse/uprotocol/transport/UTransport.kt[uTransport] interface. |=== diff --git a/pom.xml b/pom.xml index 24858b8..126e50c 100644 --- a/pom.xml +++ b/pom.xml @@ -15,35 +15,26 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - org.eclipse.uprotocol up-kotlin Kotlin Library for uProtocol Language specific uProtocol library for building and using UUri, UUID, UAttributes, UTransport, and more - 1.5.7-SNAPSHOT + 0.1.8-SNAPSHOT jar https://github.com/eclipse-uprotocol/up-kotlin/ 1.9.21 UTF-8 - 11 - 11 UTF-8 - - 1.9.1 5.9.1 4.12 3.0.0-M5 - 2.6.18 - - 2.4.2 - 3.21.10 - uprotocol-core-api-1.5.7 + main @@ -125,8 +116,23 @@ report + + + + org/eclipse/uprotocol/core/** + org/eclipse/uprotocol/v1/** + + + + + **/transport/** + **/uri/** + **/uuid/** + **/validation/** + + @@ -210,8 +216,8 @@ clone --branch ${git.tag.name} - https://github.com/eclipse-uprotocol/up-core-api.git - ${project.build.directory}/up-core-api + https://github.com/eclipse-uprotocol/up-spec.git + ${project.build.directory}/up-spec @@ -228,7 +234,7 @@ git - ${project.build.directory}/up-core-api + ${project.build.directory}/up-spec/up-core-api pull origin @@ -269,7 +275,7 @@ 11 11 - 3.10.1 + 3.13.0 org.xolstice.maven.plugins @@ -294,8 +300,7 @@ - ${project.build.directory}/up-core-api/uprotocol - + ${project.build.directory}/up-spec/up-core-api com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} @@ -310,12 +315,6 @@ kotlin-stdlib ${kotlin.version} - - io.cloudevents - cloudevents-protobuf - 2.4.2 - - com.google.errorprone @@ -323,7 +322,6 @@ 2.11.0 - org.checkerframework checker-qual @@ -338,16 +336,6 @@ pom - - - - io.cloudevents - cloudevents-bom - ${cloudevents.version} - pom - import - - nl.jqno.equalsverifier equalsverifier @@ -401,27 +389,6 @@ test - - - io.cloudevents - cloudevents-protobuf - - - - - - io.cloudevents - cloudevents-core - - - io.cloudevents - cloudevents-api - - - io.cloudevents - cloudevents-json-jackson - - com.google.protobuf protobuf-kotlin @@ -470,13 +437,13 @@ - - - dash-licenses-snapshots - https://repo.eclipse.org/content/repositories/dash-licenses-releases/ - + + + + + - + release diff --git a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/README.adoc b/src/main/kotlin/org/eclipse/uprotocol/cloudevent/README.adoc deleted file mode 100644 index 7c61f24..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/README.adoc +++ /dev/null @@ -1,109 +0,0 @@ -= uProtocol CloudEvents -:toc: -:sectnums: - - -== Overview - -https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/up-l1/cloudevents.adoc[uProtocol CloudEvents] is a common message envelope that could be used to carry way to represent uProtocol transport layer information `UUri` (source), `UPayload`, and `UAttributes`. `CloudEvents` are used by a number of Device-2-Cloud and Cloud-2-Device based transports such as MQTT and HTTP, however it could also be used by any transport (ex. Binder). - - -=== CloudEventFactory -Factory class that builds the various types of CloudEvents for uProtocol (publish, notification, request, response) - -== Examples - - -=== Building an uuri -[source,kotlin] ----- - val use: UEntity = uEntity { name = "body.access" } - val res: UResource = uResource { - name = "door" - instance = "front_left" - message = "Door" - } - val uri: UUri = uUri { - entity = use - resource = res - } - source: String = LongUriSerializer.instance().serialize(uri) ----- - -=== Build proto payload -[source,kotlin] - ----- - - val cloudEventProto: io.cloudevents.v1.proto.CloudEvent = io.cloudevents.v1.proto.CloudEvent.newBuilder() - .setSpecVersion("1.0") - .setId("hello") - .setSource("https://example.com") - .setType("example.demo") - .setProtoData(Any.newBuilder().build()) - .build() - val proto_payload: Any = Any.pack(cloudEventProto) - - ----- - -=== Build UCloudEvent Attributes -[source,kotlin] - ----- - -val uCloudEventAttributes: UCloudEventAttributes = UCloudEventAttributes.UCloudEventAttributesBuilder() - .withHash("somehash") - .withPriority(UPriority.UPRIORITY_CS1) - .withTtl(3) - .withToken("someOAuthToken") - .build() - ----- - -=== Build publish cloud event -[source,kotlin] - ----- - - -val cloudEvent: CloudEvent = CloudEventFactory.publish(source, protoPayload, uCloudEventAttributes) -# test all attributes - assertEquals("1.0", cloudEvent.getSpecVersion().toString()) - assertNotNull(cloudEvent.getId()) - assertEquals(source, cloudEvent.getSource().toString()) - assertEquals(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH), cloudEvent.getType()) - assertFalse(cloudEvent.getExtensionNames().contains("sink")) - assertEquals("somehash", cloudEvent.getExtension("hash")) - assertEquals(UPriority.UPRIORITY_CS1.name(), cloudEvent.getExtension("priority")) - assertEquals(3, cloudEvent.getExtension("ttl")) - assertArrayEquals(protoPayload.toByteArray(), Objects.requireNonNull(cloudEvent.getData()).toBytes()) - - ----- - - -=== Build cloudevent ↔ umessage -[source,kotlin] - ----- - -val result: UMessage = UCloudEvent.toMessage(cloudEvent) -assertNotNull(result) -assertTrue(UCloudEvent.getRequestId(cloudEvent).isPresent()) -assertTrue(UCloudEvent.getTtl(cloudEvent).isPresent()) -assertEquals(UCloudEvent.getTtl(cloudEvent), result.getAttributes().getTtl()) -assertEquals(UCloudEvent.getPayload(cloudEvent).toByteString(),result.getPayload().getValue()) -assertEquals(UCloudEvent.getSource(cloudEvent),LongUriSerializer.instance().serialize(result.getSource())) -assertTrue(UCloudEvent.getPriority(cloudEvent).isPresent()) -assertEquals(UCloudEvent.getPriority(cloudEvent), String.valueOf(result.getAttributes().getPriority())) - -val cloudEvent1: CloudEvent = UCloudEvent.fromMessage(result) -assertEquals(cloudEvent,cloudEvent1) - - ----- - - - - diff --git a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/datamodel/UCloudEventAttributes.kt b/src/main/kotlin/org/eclipse/uprotocol/cloudevent/datamodel/UCloudEventAttributes.kt deleted file mode 100644 index e1aa57f..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/datamodel/UCloudEventAttributes.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.cloudevent.datamodel - -import org.eclipse.uprotocol.v1.UPriority - -/** - * Specifies the properties that can configure the UCloudEvent. - * @param hash An HMAC generated on the data portion of the CloudEvent message using the device key. - * @param priority uProtocol Prioritization classifications. - * @param ttl How long this event should live for after it was generated (in milliseconds). - * Events without this attribute (or value is 0) MUST NOT timeout. - * @param token Oauth2 access token to perform the access request defined in the request message. - * @param traceparent An identifier used to correlate observability across related events. - */ -data class UCloudEventAttributes internal constructor( - val hash: String? = null, - val priority: UPriority? = null, - val ttl: Int? = null, - val token: String? = null, - val traceparent: String? = null -) { - private constructor(builder: UCloudEventAttributesBuilder) : this( - if (builder.hash.isNullOrBlank()) null else builder.hash, - builder.priority, - builder.ttl, - if (builder.token.isNullOrBlank()) null else builder.token, - builder.traceparent - ) - - /** - * An HMAC generated on the data portion of the CloudEvent message using the device key. - */ - val isEmpty: Boolean - /** - * Indicates that there are no added additional attributes to configure when building a CloudEvent. - * @return Returns true if this attributes container is an empty container and has no valuable information in building a CloudEvent. - */ - get() = hash == null && priority == null && ttl == null && token == null && traceparent == null - - /** - * Builder for constructing the UCloudEventAttributes. - */ - class UCloudEventAttributesBuilder @PublishedApi internal constructor() { - /** - * add an HMAC generated on the data portion of the CloudEvent message using the device key. - */ - var hash: String? = null - - /** - * add a uProtocol Prioritization classifications. - */ - var priority: UPriority? = null - - /** - * add a time to live which is how long this event should live for after it was generated (in milliseconds). - * Events without this attribute (or value is 0) MUST NOT timeout. - */ - var ttl: Int? = null - - /** - * Add an Oauth2 access token to perform the access request defined in the request message. - */ - var token: String? = null - - /** - * Add an identifier used to correlate observability across related events. - */ - var traceparent: String? = null - - - /** - * Construct the UCloudEventAttributes from the builder. - * @return Returns a constructed UProperty. - */ - @JvmSynthetic - @PublishedApi - internal fun build(): UCloudEventAttributes { - // validation if needed - return UCloudEventAttributes(this) - } - } - - companion object { - val EMPTY = UCloudEventAttributes() - - @JvmName("-initializeUCloudEventAttributes") - inline fun uCloudEventAttributes(block: UCloudEventAttributesBuilder.() -> Unit) = - UCloudEventAttributesBuilder().apply(block).build() - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/factory/CloudEventFactory.kt b/src/main/kotlin/org/eclipse/uprotocol/cloudevent/factory/CloudEventFactory.kt deleted file mode 100644 index eadc94d..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/factory/CloudEventFactory.kt +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.cloudevent.factory - -import com.google.protobuf.Any -import com.google.protobuf.Empty -import io.cloudevents.CloudEvent -import io.cloudevents.core.builder.CloudEventBuilder -import org.eclipse.uprotocol.cloudevent.datamodel.UCloudEventAttributes -import org.eclipse.uprotocol.uri.Uri -import org.eclipse.uprotocol.uuid.factory.UUIDV8 -import org.eclipse.uprotocol.uuid.serializer.LongUuidSerializer -import org.eclipse.uprotocol.v1.UMessageType -import org.eclipse.uprotocol.v1.UUID -import org.eclipse.uprotocol.v1.UUri -import java.net.URI - -/** - * A factory is a part of the software has methods to generate concrete objects, usually of the same type or interface.

- * CloudEvents is a specification for describing events in a common way. We will use CloudEvents - * to formulate all kinds of events (messages) that will be sent to and from devices.

- * The CloudEvent factory knows how to generate CloudEvents of the 4 core types: req.v1, res.v1, pub.v1, and file.v1

- */ -object CloudEventFactory { - /** - * Create a CloudEvent for an event for the use case of: RPC Request message. - * - * @param applicationUriForRPC The [Uri] for the application requesting the RPC. - * @param serviceMethodUri The [Uri] for the method to be called on the service Ex.: :/body.access/1/rpc.UpdateDoor - * @param protoPayload Protobuf Any object with the Message command to be executed on the sink service. - * @param attributes Additional attributes such as ttl, hash, priority and token. - * @return Returns an request CloudEvent. - */ - fun request( - applicationUriForRPC: Uri, - serviceMethodUri: Uri, - protoPayload: Any, - attributes: UCloudEventAttributes - ): CloudEvent { - val id = generateCloudEventId() - return buildBaseCloudEvent( - id, - applicationUriForRPC, - protoPayload.toByteArray(), - protoPayload.typeUrl, - attributes - ) - .withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_REQUEST)) - .withExtension("sink", URI.create(serviceMethodUri.value)) - .build() - } - - /** - * Create a CloudEvent for an event for the use case of: RPC Response message. - * - * @param applicationUriForRPC The destination of the response. The [Uri] for the original application that requested the RPC and this response is for. - * @param serviceMethodUri The [Uri] for the method that was called on the service Ex.: :/body.access/1/rpc.UpdateDoor - * @param requestId The cloud event id from the original request cloud event that this response if for. - * @param protoPayload The protobuf serialized response message as defined by the application interface or the - * google.rpc.Status message containing the details of an error. - * @param attributes Additional attributes such as ttl, hash and priority. - * @return Returns an response CloudEvent. - */ - fun response( - applicationUriForRPC: Uri, - serviceMethodUri: Uri, - requestId: String, - protoPayload: Any, - attributes: UCloudEventAttributes - ): CloudEvent { - val id = generateCloudEventId() - return buildBaseCloudEvent( - id, - serviceMethodUri, - protoPayload.toByteArray(), - protoPayload.typeUrl, - attributes - ) - .withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_RESPONSE)) - .withExtension("sink", URI.create(applicationUriForRPC.value)) - .withExtension("reqid", requestId) - .build() - } - - /** - * Create a CloudEvent for an event for the use case of: RPC Response message that failed. - * - * @param applicationUriForRPC The destination of the response. The [Uri] for the original application that requested the RPC and this response is for. - * @param serviceMethodUri The [Uri] for the method that was called on the service Ex.: :/body.access/1/rpc.UpdateDoor - * @param requestId The cloud event id from the original request cloud event that this response if for. - * @param communicationStatus A UCode value that indicates of a platform communication error while delivering this CloudEvent. - * @param attributes Additional attributes such as ttl, hash and priority. - * @return Returns a response CloudEvent Response for the use case of RPC Response message that failed. - */ - fun failedResponse( - applicationUriForRPC: Uri, - serviceMethodUri: Uri, - requestId: String, - communicationStatus: Int, - attributes: UCloudEventAttributes - ): CloudEvent { - val id = generateCloudEventId() - val protoPayload: Any = Any.pack(Empty.getDefaultInstance()) - return buildBaseCloudEvent( - id, - serviceMethodUri, - protoPayload.toByteArray(), - protoPayload.typeUrl, - attributes - ) - .withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_RESPONSE)) - .withExtension("sink", URI.create(applicationUriForRPC.value)) - .withExtension("reqid", requestId) - .withExtension("commstatus", communicationStatus) - .build() - } - - /** - * Create a CloudEvent for an event for the use case of: Publish generic message. - * - * @param source The [Uri] of the topic being published. - * @param protoPayload protobuf Any object with the Message to be published. - * @param attributes Additional attributes such as ttl, hash and priority. - * @return Returns a publish CloudEvent. - */ - fun publish(source: Uri, protoPayload: Any, attributes: UCloudEventAttributes): CloudEvent { - val id = generateCloudEventId() - return buildBaseCloudEvent(id, source, protoPayload.toByteArray(), protoPayload.typeUrl, attributes) - .withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - .build() - } - - /** - * Create a CloudEvent for an event for the use case of: Publish a notification message.

- * A published event containing the sink (destination) is often referred to as a notification, it is an event sent to a specific consumer. - * - * @param source The [Uri] of the topic being published. - * @param sink The [Uri] of the destination of this notification. - * @param protoPayload protobuf Any object with the Message to be published. - * @param attributes Additional attributes such as ttl, hash and priority. - * @return Returns a publish CloudEvent. - */ - fun notification( - source: Uri, - sink: Uri, - protoPayload: Any, - attributes: UCloudEventAttributes - ): CloudEvent { - val id = generateCloudEventId() - return buildBaseCloudEvent(id, source, protoPayload.toByteArray(), protoPayload.typeUrl, attributes) - .withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - .withExtension("sink", URI.create(sink.value)) - .build() - } - - /** - * @return Returns a UUIDv8 id. - */ - private fun generateCloudEventId(): String { - val uuid: UUID = UUIDV8() - return LongUuidSerializer.INSTANCE.serialize(uuid) - } - - /** - * Base CloudEvent builder that is the same for all CloudEvent types. - * - * @param id Event unique identifier. - * @param source Identifies who is sending this event in the format of a uProtocol URI that - * can be built from a [UUri] object. - * @param protoPayloadBytes The serialized Event data with the content type of "application/x-protobuf". - * @param protoPayloadSchema The schema of the proto payload bytes, for example you can use `protoPayload.typeUrl` on your service/app object. - * @param attributes Additional cloud event attributes that can be passed in. All attributes are optional and will be added only if they - * were configured. - * @return Returns a CloudEventBuilder that can be additionally configured and then by calling .build() construct a CloudEvent - * ready to be serialized and sent to the transport layer. - */ - fun buildBaseCloudEvent( - id: String?, - source: Uri, - protoPayloadBytes: ByteArray, - protoPayloadSchema: String?, - attributes: UCloudEventAttributes - ): CloudEventBuilder { - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1() - .withId(id) - .withSource(URI.create(source.value)) /* Not needed: - .withDataContentType(PROTOBUF_CONTENT_TYPE) - .withDataSchema(URI.create(protoPayloadSchema)) - */ - .withData(protoPayloadBytes) - attributes.ttl?.let { ttl -> cloudEventBuilder.withExtension("ttl", ttl) } - attributes.priority?.let { priority -> - cloudEventBuilder.withExtension( - "priority", - UCloudEvent.getCeName(priority.valueDescriptor) - ) - } - attributes.hash?.let { hash -> cloudEventBuilder.withExtension("hash", hash) } - attributes.token?.let { token -> cloudEventBuilder.withExtension("token", token) } - attributes.traceparent?.let { traceparent: String -> - cloudEventBuilder.withExtension("traceparent", traceparent) - } - - return cloudEventBuilder - } - - const val PROTOBUF_CONTENT_TYPE = "application/x-protobuf" -} - diff --git a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/factory/UCloudEvent.kt b/src/main/kotlin/org/eclipse/uprotocol/cloudevent/factory/UCloudEvent.kt deleted file mode 100644 index 14f7327..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/factory/UCloudEvent.kt +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.cloudevent.factory - -import com.google.protobuf.Any -import com.google.protobuf.Descriptors.EnumValueDescriptor -import com.google.protobuf.InvalidProtocolBufferException -import com.google.protobuf.Message -import io.cloudevents.CloudEvent -import io.cloudevents.CloudEventData -import io.cloudevents.core.builder.CloudEventBuilder -import org.eclipse.uprotocol.UprotocolOptions -import org.eclipse.uprotocol.uri.Uri -import org.eclipse.uprotocol.uri.serializer.LongUriSerializer -import org.eclipse.uprotocol.uuid.factory.getTime -import org.eclipse.uprotocol.uuid.factory.isUuid -import org.eclipse.uprotocol.uuid.serializer.LongUuidSerializer -import org.eclipse.uprotocol.v1.* -import java.net.URI -import java.time.OffsetDateTime -import java.time.temporal.ChronoUnit - - -/** - * Class to extract information from a CloudEvent. - */ -object UCloudEvent { - /** - * Extract the source from a cloud event. The source is a mandatory attribute. - * The CloudEvent constructor does not allow creating a cloud event without a source. - * @param cloudEvent CloudEvent with source to be extracted. - * @return Returns the String value of a CloudEvent source attribute. - */ - fun getSource(cloudEvent: CloudEvent): String { - return cloudEvent.source.toString() - } - - /** - * Extract the sink from a cloud event. The sink attribute is optional. - * @param cloudEvent CloudEvent with sink to be extracted. - * @return Returns a Uri String value of a CloudEvent sink attribute if it exists, - * otherwise Null is returned. - */ - fun getSink(cloudEvent: CloudEvent): Uri? { - return extractStringValueFromExtension("sink", cloudEvent)?.let { Uri(it) } - } - - /** - * Extract the request id from a cloud event that is a response RPC CloudEvent. The attribute is optional. - * @param cloudEvent the response RPC CloudEvent with request id to be extracted. - * @return Returns a String value of a response RPC CloudEvent request id attribute if it exists, - * otherwise Null is returned. - */ - fun getRequestId(cloudEvent: CloudEvent): String? { - return extractStringValueFromExtension("reqid", cloudEvent) - } - - /** - * Extract the hash attribute from a cloud event. The hash attribute is optional. - * @param cloudEvent CloudEvent with hash to be extracted. - * @return Returns a String value of a CloudEvent hash attribute if it exists, - * otherwise Null is returned. - */ - fun getHash(cloudEvent: CloudEvent): String? { - return extractStringValueFromExtension("hash", cloudEvent) - } - - /** - * Extract the string value of the priority attribute from a cloud event. The priority attribute is optional. - * @param cloudEvent CloudEvent with priority to be extracted. - * @return Returns a String value of a CloudEvent priority attribute if it exists, - * otherwise Null is returned. - */ - fun getPriority(cloudEvent: CloudEvent): String? { - return extractStringValueFromExtension("priority", cloudEvent) - } - - /** - * Extract the integer value of the ttl attribute from a cloud event. The ttl attribute is optional. - * @param cloudEvent CloudEvent with ttl to be extracted. - * @return Returns an Int value of a CloudEvent ttl attribute if it exists, - * otherwise Null is returned. - */ - fun getTtl(cloudEvent: CloudEvent): Int? { - return extractStringValueFromExtension("ttl", cloudEvent)?.toInt() - } - - /** - * Extract the string value of the token attribute from a cloud event. The token attribute is optional. - * @param cloudEvent CloudEvent with token to be extracted. - * @return Returns a String value of a CloudEvent priority token if it exists, - * otherwise Null is returned. - */ - fun getToken(cloudEvent: CloudEvent): String? { - return extractStringValueFromExtension("token", cloudEvent) - } - - - /** - * Extract the string value of the traceparent attribute from a cloud event. The traceparent attribute is optional. - * @param cloudEvent CloudEvent with traceparent to be extracted. - * @return Returns a String value of a CloudEvent traceparent if it exists, - * otherwise Null is returned. - */ - fun getTraceparent(cloudEvent: CloudEvent): String? { - return extractStringValueFromExtension("traceparent", cloudEvent) - } - - /** - * Fetch the UCode from the CloudEvent commstatus integer value. The communication status attribute is optional. - * If there was a platform communication error that occurred while delivering this cloudEvent, it will be indicated in this attribute. - * If the attribute does not exist, it is assumed that everything was UCode.OK_VALUE. - * @param cloudEvent CloudEvent with the platformError to be extracted. - * @return Returns a UCode that indicates of a platform communication error while delivering this CloudEvent or UCode.OK. - */ - fun getCommunicationStatus(cloudEvent: CloudEvent): UCode { - return try { - UCode.forNumber(extractIntegerValueFromExtension("commstatus", cloudEvent) ?: UCode.OK_VALUE) - } catch (e: Exception) { - UCode.OK - } - } - - /** - * Indication of a platform communication error that occurred while trying to deliver the CloudEvent. - * @param cloudEvent CloudEvent to be queried for a platform delivery error. - * @return returns true if the provided CloudEvent is marked with having a platform delivery problem. - */ - fun hasCommunicationStatusProblem(cloudEvent: CloudEvent): Boolean { - return getCommunicationStatus(cloudEvent) != UCode.OK - } - - /** - * Returns a new CloudEvent from the supplied CloudEvent, with the platform communication added. - * @param cloudEvent CloudEvent that the platform delivery error will be added. - * @param communicationStatus the platform delivery error UCode to add to the CloudEvent. - * @return Returns a new CloudEvent from the supplied CloudEvent, with the platform communication added. - */ - fun addCommunicationStatus(cloudEvent: CloudEvent, communicationStatus: Int?): CloudEvent { - if (communicationStatus == null) { - return cloudEvent - } - val builder: CloudEventBuilder = CloudEventBuilder.v1(cloudEvent) - builder.withExtension("commstatus", communicationStatus) - return builder.build() - } - - /** - * Extract the timestamp from the UUIDV8 CloudEvent ID, with Unix epoch as the - * @param cloudEvent The CloudEvent with the timestamp to extract. - * @return Return the timestamp from the UUIDV8 CloudEvent ID or a Null if timestamp can't be extracted. - */ - fun getCreationTimestamp(cloudEvent: CloudEvent): Long? { - val cloudEventId = cloudEvent.id - val uuid = LongUuidSerializer.INSTANCE.deserialize(cloudEventId) - return uuid.getTime() - } - - /** - * Calculate if a CloudEvent configured with a creation time and a ttl attribute is expired.

- * The ttl attribute is a configuration of how long this event should live for after it was generated (in milliseconds) - * @param cloudEvent The CloudEvent to inspect for being expired. - * @return Returns true if the CloudEvent was configured with a ttl > 0 and a creation time to compare for expiration. - */ - fun isExpiredByCloudEventCreationDate(cloudEvent: CloudEvent): Boolean { - val ttl = getTtl(cloudEvent) ?: return false - if (ttl <= 0) { - return false - } - val cloudEventCreationTime: OffsetDateTime = cloudEvent.time ?: return false - val now: OffsetDateTime = OffsetDateTime.now() - val creationTimePlusTtl: OffsetDateTime = cloudEventCreationTime.plus(ttl.toLong(), ChronoUnit.MILLIS) - return now.isAfter(creationTimePlusTtl) - } - - /** - * Calculate if a CloudEvent configured with UUIDv8 id and a ttl attribute is expired.

- * The ttl attribute is a configuration of how long this event should live for after it was generated (in milliseconds) - * @param cloudEvent The CloudEvent to inspect for being expired. - * @return Returns true if the CloudEvent was configured with a ttl > 0 and UUIDv8 id to compare for expiration. - */ - fun isExpired(cloudEvent: CloudEvent): Boolean { - val ttl = getTtl(cloudEvent) ?: return false - if (ttl <= 0) { - return false - } - val cloudEventId: String = cloudEvent.id - val uuid = LongUuidSerializer.INSTANCE.deserialize(cloudEventId) - if (uuid == UUID.getDefaultInstance()) { - return false - } - val delta: Long = System.currentTimeMillis() - (uuid.getTime() ?: 0L) - return delta >= ttl - } - - /** - * Check if a CloudEvent is a valid UUIDv6 or v8 . - * @param cloudEvent The CloudEvent with the id to inspect. - * @return Returns true if the CloudEvent is valid. - */ - fun isCloudEventId(cloudEvent: CloudEvent): Boolean { - val cloudEventId: String = cloudEvent.id - val uuid = LongUuidSerializer.INSTANCE.deserialize(cloudEventId) - return uuid.isUuid() - } - - /** - * Extract the payload from the CloudEvent as a protobuf Any object.

- * An all or nothing error handling strategy is implemented. If anything goes wrong, an Any.getDefaultInstance() will be returned. - * @param cloudEvent CloudEvent containing the payload to extract. - * @return Extracts the payload from a CloudEvent as a Protobuf Any object. - */ - fun getPayload(cloudEvent: CloudEvent): Any { - val data: CloudEventData = cloudEvent.data ?: return Any.getDefaultInstance() - return try { - Any.parseFrom(data.toBytes()) - } catch (e: InvalidProtocolBufferException) { - Any.getDefaultInstance() - } - } - - /** - * Extract the payload from the CloudEvent as a protobuf Message of the provided class. The protobuf of this message - * class must be loaded on the client for this to work.

- * An all or nothing error handling strategy is implemented. If anything goes wrong, a Null will be returned.

- * Example:

- *
SomeMessage; unpacked = UCloudEvent.unpack(cloudEvent, SomeMessage::class.java);
- * @param cloudEvent CloudEvent containing the payload to extract. - * @param clazz The class that extends [Message] that the payload is extracted into. - * @return Returns a [Message] payload of the class type that is provided. - * @param The class type of the Message to be unpacked. - */ - fun unpack(cloudEvent: CloudEvent, clazz: Class): T? { - return try { - getPayload(cloudEvent).unpack(clazz) - } catch (e: InvalidProtocolBufferException) { - // All or nothing error handling strategy. If something goes wrong, you just get an empty. - null - } - } - - /** - * Function used to pretty print a CloudEvent containing only the id, source, type and maybe a sink. Used mainly for logging. - * @param cloudEvent The CloudEvent we want to pretty print. - * @return returns the String representation of the CloudEvent containing only the id, source, type and maybe a sink. - */ - fun toString(cloudEvent: CloudEvent): String { - val sink = getSink(cloudEvent)?.let { ", sink='$it'" } ?: "" - return "CloudEvent{id='${cloudEvent.id}', source='${cloudEvent.source}'${sink}, type='${cloudEvent.type}'}" - - } - - /** - * Utility for extracting the String value of an extension. - * @param extensionName The name of the CloudEvent extension. - * @param cloudEvent The CloudEvent containing the data. - * @return returns the String value of the extension matching the extension name, - * or a Null is the value does not exist. - */ - private fun extractStringValueFromExtension(extensionName: String, cloudEvent: CloudEvent): String? { - return cloudEvent.extensionNames?.let { - if (it.contains(extensionName)) { - cloudEvent.getExtension(extensionName)?.toString() - } else { - null - } - } - } - - /** - * Utility for extracting the Integer value of an extension. - * @param extensionName The name of the CloudEvent extension. - * @param cloudEvent The CloudEvent containing the data. - * @return returns the Integer value of the extension matching the extension name, - * or a Null is the value does not exist. - */ - private fun extractIntegerValueFromExtension(extensionName: String, cloudEvent: CloudEvent): Int? { - return extractStringValueFromExtension(extensionName, cloudEvent)?.toInt() - } - - /** - * Get the string representation of the UMessageType. - * - * Note: The UMessageType is determined by the type of the CloudEvent. If - * the UMessageType is UMESSAGE_TYPE_NOTIFICATION, we assume the CloudEvent type - * is "pub.v1" and the sink is present. - * @param type The UMessageType - * @return returns the string representation of the UMessageType - * - */ - fun getEventType(type: UMessageType): String { - return getCeName(type.valueDescriptor) - } - - /** - * Get the string representation of the UPriority - * @param priority - * @return returns the string representation of the UPriority - */ - fun getCePriority(priority: UPriority): String { - return getCeName(priority.valueDescriptor) - } - - /** - * Get the UPriority from the string name - * @param priority - * @return returns the UPriority - */ - fun getUPriority(priority: String, default : UPriority = UPriority.UNRECOGNIZED): UPriority { - return UPriority.getDescriptor().values - .filter { value -> - value.options.hasExtension(UprotocolOptions.ceName) && value.options - .getExtension(UprotocolOptions.ceName) == priority - }.map { value -> - UPriority.forNumber(value.number) - }.firstOrNull() ?: default - } - - /** - * Get the UMessageType from the string representation. - * - * Note: The UMessageType is determined by the type of the CloudEvent. - * If the CloudEvent type is "pub.v1" and the sink is present, the UMessageType is assumed to be - * UMESSAGE_TYPE_NOTIFICATION, this is because uProtocol CloudEvent definition did not have an explicit - * notification type. - * - * @return returns the UMessageType - */ - fun getMessageType(type: String): UMessageType { - return UMessageType.getDescriptor().values - .filter { value -> - value.options.hasExtension(UprotocolOptions.ceName) && value.options - .getExtension(UprotocolOptions.ceName) == type - }.map { value -> - UMessageType.forNumber(value.number) - }.firstOrNull() ?: UMessageType.UNRECOGNIZED - } - - /** - * Get the UMessage from the cloud event - * @param event The CloudEvent containing the data. - * @return returns the UMessage - */ - fun toMessage(event: CloudEvent): UMessage { - - val msgPayload = uPayload { - format = getUPayloadFormatFromContentType(event.dataContentType) - value = getPayload(event).toByteString() - } - - val msgAttributes = uAttributes { - id = LongUuidSerializer.INSTANCE.deserialize(event.id) - type = getMessageType(event.type) - source = LongUriSerializer.INSTANCE.deserialize(event.source.toString()) - if (hasCommunicationStatusProblem(event)) { - commstatus = getCommunicationStatus(event) - } - getPriority(event)?.let { p -> - priority = getUPriority(p, UPriority.UPRIORITY_UNSPECIFIED) - } - - getSink(event)?.let { sink = LongUriSerializer.INSTANCE.deserialize(it.value) } - - getRequestId(event)?.let { reqid = LongUuidSerializer.INSTANCE.deserialize(it) } - - getTtl(event)?.let { ttl = it } - - getToken(event)?.let { token = it } - - getTraceparent(event)?.let { traceparent = it } - - extractIntegerValueFromExtension("plevel", event)?.let { permissionLevel = it } - - } - return uMessage { - attributes = msgAttributes - payload = msgPayload - } - } - - /** - * Get the Cloudevent from the UMessage
- * Note: For now, only the value format of UPayload is supported in the SDK.If the UPayload has a reference, it - * needs to be copied to CloudEvent. - * @param message The UMessage protobuf containing the data - * @return returns the cloud event - */ - fun fromMessage(message: UMessage): CloudEvent { - val attributes: UAttributes = message.attributes ?: UAttributes.getDefaultInstance() - val payload = message.payload ?: UPayload.getDefaultInstance() - val builder: CloudEventBuilder = - CloudEventBuilder.v1().withId(LongUuidSerializer.INSTANCE.serialize(attributes.id)) - builder.withType(getEventType(attributes.type)) - builder.withSource(URI.create(LongUriSerializer.INSTANCE.serialize(attributes.source))) - val contentType = getContentTypeFromUPayloadFormat(payload.format) - if (contentType.isNotEmpty()) { - builder.withDataContentType(contentType) - } - // IMPORTANT: Currently, ONLY the VALUE format is supported in the SDK! - if (payload.hasValue()) { - builder.withData(payload.value.toByteArray()) - } - - if (attributes.hasTtl()) { - builder.withExtension("ttl", attributes.ttl) - } - if (attributes.hasToken()) { - builder.withExtension("token", attributes.token) - } - - if (attributes.priorityValue > 0) { - builder.withExtension("priority", getCePriority(attributes.priority)) - } - - if (attributes.hasSink()) { - builder.withExtension("sink", URI.create(LongUriSerializer.INSTANCE.serialize(attributes.sink))) - } - - if (attributes.hasCommstatus()) { - builder.withExtension("commstatus", attributes.commstatus.number) - } - - if (attributes.hasReqid()) { - builder.withExtension("reqid", LongUuidSerializer.INSTANCE.serialize(attributes.reqid)) - } - - if (attributes.hasPermissionLevel()) { - builder.withExtension("plevel", attributes.permissionLevel) - } - - if (attributes.hasTraceparent()) { - builder.withExtension("traceparent", attributes.traceparent) - } - - return builder.build() - - } - - /** - * Retrieves the payload format enumeration based on the provided string representation of the data content type
- * This method uses the uProtocol mimeType custom options declared in upayload.proto. - * - * @param contentType The content type string representing the format of the payload. - * @return The corresponding UPayloadFormat enumeration based on the content type. - */ - private fun getUPayloadFormatFromContentType(contentType: String?): UPayloadFormat { - if (contentType == null) { - return UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY - } - return UPayloadFormat.getDescriptor().values.filter { v: EnumValueDescriptor -> - v.options.hasExtension(UprotocolOptions.mimeType) && v.options - .getExtension(UprotocolOptions.mimeType) == contentType - }.map { v: EnumValueDescriptor -> - UPayloadFormat.forNumber(v.number) - }.firstOrNull() ?: UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY - } - - /** - * Retrieves the string representation of the data content type based on the provided UPayloadFormat.

- * This method uses the uProtocol mimeType custom options declared in upayload.proto. - * - * @param format The UPayloadFormat enumeration representing the payload format. - * @return The corresponding content type string based on the payload format. - */ - fun getContentTypeFromUPayloadFormat(format: UPayloadFormat): String { - // Since the default value is UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY, we return an empty string. - if (format == UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY) { - return "" - } - return format.valueDescriptor.options.getExtension(UprotocolOptions.mimeType) - } - - /** - * Retrieves the string representation of the data content type based on the provided Enum value descriptor.

- * - * @param descriptor The EnumDescriptor enumeration representing the payload format. - * @return The corresponding string name for the value. - */ - fun getCeName(descriptor: EnumValueDescriptor): String { - return descriptor.options.getExtension(UprotocolOptions.ceName) - } -} - diff --git a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/Base64ProtobufSerializer.kt b/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/Base64ProtobufSerializer.kt deleted file mode 100644 index caa9b59..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/Base64ProtobufSerializer.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.cloudevent.serialize - -import java.util.Base64 - -/** - * Helper for serializing Base64 protobuf data. - */ -object Base64ProtobufSerializer { - /** - * Deserialize a base64 protobuf payload into a Base64 String. - * @param bytes byte[] data - * @return Returns a String from the base64 protobuf payload. - */ - fun deserialize(bytes: ByteArray?): String { - return if (bytes == null) { - "" - } else Base64.getEncoder().encodeToString(bytes) - } - - /** - * Serialize a String into Base64 format. - * @param stringToSerialize String to serialize. - * @return Returns the Base64 formatted String as a byte[]. - */ - fun serialize(stringToSerialize: String?): ByteArray { - return if (stringToSerialize == null) { - ByteArray(0) - } else Base64.getDecoder().decode(stringToSerialize.toByteArray()) - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventSerializer.kt b/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventSerializer.kt deleted file mode 100644 index 6bf03c4..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventSerializer.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2023 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.cloudevent.serialize - -import io.cloudevents.CloudEvent - -interface CloudEventSerializer { - fun serialize(cloudEvent: CloudEvent): ByteArray - fun deserialize(bytes: ByteArray): CloudEvent -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventSerializers.kt b/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventSerializers.kt deleted file mode 100644 index 1b7cdbd..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventSerializers.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.cloudevent.serialize - -/** - * Provides Singleton instances of the CloudEvent Serializers. - */ -enum class CloudEventSerializers(private val cloudEventSerializer: CloudEventSerializer) { - JSON(CloudEventToJsonSerializer()), - PROTOBUF(CloudEventToProtobufSerializer()); - - fun serializer(): CloudEventSerializer { - return cloudEventSerializer - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventToJsonSerializer.kt b/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventToJsonSerializer.kt deleted file mode 100644 index 8f0299a..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventToJsonSerializer.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.cloudevent.serialize - -import io.cloudevents.CloudEvent -import io.cloudevents.jackson.JsonFormat - -/** - * CloudEventSerializer to serialize and deserialize CloudEvents to JSON format. - */ -class CloudEventToJsonSerializer : CloudEventSerializer { - override fun serialize(cloudEvent: CloudEvent): ByteArray { - return serializer.serialize(cloudEvent) - } - - override fun deserialize(bytes: ByteArray): CloudEvent { - return serializer.deserialize(bytes) - } - - companion object { - // Force database64 encoding as we know the data will be in a protobuf format - private val serializer: JsonFormat = JsonFormat(true, false) - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventToProtobufSerializer.kt b/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventToProtobufSerializer.kt deleted file mode 100644 index 68ffd10..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventToProtobufSerializer.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.cloudevent.serialize - -import io.cloudevents.CloudEvent -import io.cloudevents.protobuf.ProtobufFormat - -/** - * CloudEventSerializer to serialize and deserialize CloudEvents to protobuf format. - */ -class CloudEventToProtobufSerializer : CloudEventSerializer { - override fun serialize(cloudEvent: CloudEvent): ByteArray { - return serializer.serialize(cloudEvent) - } - - override fun deserialize(bytes: ByteArray): CloudEvent { - return serializer.deserialize(bytes) - } - - companion object { - private val serializer: ProtobufFormat = ProtobufFormat() - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/validate/CloudEventValidator.kt b/src/main/kotlin/org/eclipse/uprotocol/cloudevent/validate/CloudEventValidator.kt deleted file mode 100644 index 37459b1..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/cloudevent/validate/CloudEventValidator.kt +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.cloudevent.validate - -import io.cloudevents.CloudEvent -import org.eclipse.uprotocol.cloudevent.factory.UCloudEvent -import org.eclipse.uprotocol.uri.Uri -import org.eclipse.uprotocol.uri.serializer.LongUriSerializer -import org.eclipse.uprotocol.uri.toUri -import org.eclipse.uprotocol.uri.validator.isRpcMethod -import org.eclipse.uprotocol.uri.validator.validate -import org.eclipse.uprotocol.v1.* -import org.eclipse.uprotocol.validation.ValidationResult - -/** - * Validates a CloudEvent using google.grpc.Status

- * - * [google.grpc.Status](https://grpc.github.io/grpc/core/md_doc_statuscodes.html) - */ -sealed class CloudEventValidator { - protected abstract val typeName: String - - //hardcode to prevent obfuscation - protected abstract val className: String - - /** - * Validate the CloudEvent. A CloudEventValidator instance is obtained according to the type attribute on the CloudEvent. - * @param cloudEvent The CloudEvent to validate. - * @return Returns a UStatus with success or a UUStatus with failure containing all the errors that were found. - */ - fun validate(cloudEvent: CloudEvent): ValidationResult { - val errorMessage = listOf( - validateVersion(cloudEvent), - validateId(cloudEvent), - validateSource(cloudEvent), - validateType(cloudEvent), - validateSink(cloudEvent) - ).filter { it.isFailure() }.joinToString(",") { it.getMessage() } - return if (errorMessage.isBlank()) { - ValidationResult.success() - } else { - ValidationResult.failure(errorMessage) - - } - } - - - /** - * Validate the source value of a cloud event. - * @param cloudEvent The cloud event containing the source to validate. - * @return Returns the ValidationResult containing a success or a failure with the error message. - */ - abstract fun validateSource(cloudEvent: CloudEvent): ValidationResult - - /** - * Validate the type of a cloud event. - * @param cloudEvent The cloud event containing the source to validate. - * @return Returns the ValidationResult containing a success or a failure with the error message. - */ - fun validateType(cloudEvent: CloudEvent): ValidationResult { - return if ("$typeName.v1" == cloudEvent.type) ValidationResult.success() else ValidationResult.failure( - "Invalid CloudEvent type [${cloudEvent.type}]. CloudEvent of type $className must have a type of '$typeName.v1'" - ) - } - - override fun toString(): String { - return "CloudEventValidator.$className" - } - - /** - * Validate the sink value of a cloud event in the default scenario where the sink attribute is optional. - * @param cloudEvent The cloud event containing the sink to validate. - * @return Returns the ValidationResult containing a success or a failure with the error message. - */ - open fun validateSink(cloudEvent: CloudEvent): ValidationResult { - return UCloudEvent.getSink(cloudEvent)?.let { sink -> - val checkSink = sink.validateUEntityUri() - if (checkSink.isFailure()) { - ValidationResult.failure("Invalid CloudEvent sink [${sink}]. ${checkSink.getMessage()}") - } else { - ValidationResult.success() - } - } ?: ValidationResult.success() - } - - companion object { - /** - * Obtain a CloudEventValidator according to the type attribute in the CloudEvent. - * @return Returns a CloudEventValidator according to the type attribute in the CloudEvent. - */ - fun CloudEvent.getValidator(): CloudEventValidator { - val cloudEventType: String? = type - if (cloudEventType.isNullOrEmpty()) { - return Publish - } - val validator: CloudEventValidator = when (UCloudEvent.getMessageType(cloudEventType)) { - UMessageType.UMESSAGE_TYPE_NOTIFICATION -> Notification - UMessageType.UMESSAGE_TYPE_RESPONSE -> Response - UMessageType.UMESSAGE_TYPE_REQUEST -> Request - else -> Publish - } - return validator - } - - fun validateVersion(cloudEvent: CloudEvent): ValidationResult { - return validateVersion(cloudEvent.specVersion.toString()) - } - - fun validateId(cloudEvent: CloudEvent): ValidationResult { - return if (UCloudEvent.isCloudEventId(cloudEvent)) ValidationResult.success() else ValidationResult.failure( - "Invalid CloudEvent Id [${cloudEvent.id}]. CloudEvent Id must be of type UUIDv8." - ) - } - - /** - * Validate an UriPart for a Software Entity must have an authority in the case of a microRemote uri, and must contain - * the name of the USE. - * @return Returns the ValidationResult containing a success or a failure with the error message. - */ - fun Uri.validateUEntityUri(): ValidationResult { - return LongUriSerializer.INSTANCE.deserialize(this.value).validateUEntityUri() - } - - /** - * Validate an UriPart for a Software Entity must have an authority in the case of a microRemote uri, and must contain - * the name of the USE. - * @return Returns the ValidationResult containing a success or a failure with the error message. - */ - fun UUri.validateUEntityUri(): ValidationResult { - return validate() - } - - fun validateVersion(version: String): ValidationResult { - return if (version == "1.0") ValidationResult.success() else ValidationResult.failure( - "Invalid CloudEvent version [$version]. CloudEvent version must be 1.0." - ) - } - - /** - * Validate a UriPart that is to be used as a topic in publish scenarios for events such as publish, file and notification. - * @return Returns the ValidationResult containing a success or a failure with the error message. - */ - fun Uri.validateTopicUri(): ValidationResult { - return LongUriSerializer.INSTANCE.deserialize(this.value).validateTopicUri() - } - - /** - * Validate a UriPart that is to be used as a topic in publish scenarios for events such as publish, file and notification. - * @return Returns the ValidationResult containing a success or a failure with the error message. - */ - fun UUri.validateTopicUri(): ValidationResult { - val validationResult: ValidationResult = validateUEntityUri() - if (validationResult.isFailure()) { - return validationResult - } - val uResource: UResource = resource - if (uResource.name.isBlank()) { - return ValidationResult.failure("UriPart is missing uResource name.") - } - return if (uResource.message.isEmpty()) { - ValidationResult.failure("UriPart is missing Message information.") - } else ValidationResult.success() - } - - /** - * Validate a UriPart that is meant to be used as the application response topic for rpc calls.

- * Used in Request source values and Response sink values. - * @return Returns the ValidationResult containing a success or a failure with the error message. - */ - fun Uri.validateRpcTopicUri(): ValidationResult { - return LongUriSerializer.INSTANCE.deserialize(this.value).validateRpcTopicUri() - } - - /** - * Validate an UriPart that is meant to be used as the application response topic for rpc calls.

- * @return Returns the ValidationResult containing a success or a failure with the error message. - */ - fun UUri.validateRpcTopicUri(): ValidationResult { - val validationResult: ValidationResult = validateUEntityUri() - if (validationResult.isFailure()) { - return ValidationResult.failure("Invalid RPC uri application response topic. ${validationResult.getMessage()}") - } - val uResource: UResource = resource - val topic = "${uResource.name}.${uResource.instance}" - return if ("rpc.response" != topic) { - ValidationResult.failure("Invalid RPC uri application response topic. UriPart is missing rpc.response.") - } else ValidationResult.success() - } - - /** - * Validate a UriPart that is meant to be used as an RPC method URI. Used in Request sink values and Response source values. - * @return Returns the ValidationResult containing a success or a failure with the error message. - */ - fun Uri.validateRpcMethod(): ValidationResult { - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(this.value) - val validationResult: ValidationResult = uuri.validateUEntityUri() - if (validationResult.isFailure()) { - return ValidationResult.failure("Invalid RPC method uri. ${validationResult.getMessage()}") - } - return if (!uuri.isRpcMethod()) { - ValidationResult.failure("Invalid RPC method uri. UriPart should be the method to be called, or method from response.") - } else ValidationResult.success() - } - } -} - -/** - * Implements Validations for a CloudEvent of type Publish. - */ -object Publish : CloudEventValidator() { - override val typeName: String = "pub" - override val className: String = "Publish" - - override fun validateSource(cloudEvent: CloudEvent): ValidationResult { - val source: Uri = cloudEvent.source.toString().toUri() - val checkSource = source.validateTopicUri() - return if (checkSource.isFailure()) { - ValidationResult.failure( - "Invalid Publish type CloudEvent source [${source}]. ${checkSource.getMessage()}" - ) - } else ValidationResult.success() - } -} - -/** - * Implements Validations for a CloudEvent of type Publish that behaves as a Notification, meaning - * it must have a sink. - */ -object Notification : CloudEventValidator() { - override val typeName: String = "not" - override val className: String = "Notification" - override fun validateSource(cloudEvent: CloudEvent): ValidationResult { - val source: Uri = cloudEvent.source.toString().toUri() - val checkSource = source.validateTopicUri() - return if (checkSource.isFailure()) { - ValidationResult.failure( - "Invalid Notification type CloudEvent source [${source}]. ${checkSource.getMessage()}" - ) - } else ValidationResult.success() - } - - override fun validateSink(cloudEvent: CloudEvent): ValidationResult { - val sink = UCloudEvent.getSink(cloudEvent) - ?: return ValidationResult.failure("Invalid CloudEvent sink. Notification CloudEvent sink must be an uri.") - val checkSink: ValidationResult = sink.validateUEntityUri() - if (checkSink.isFailure()) { - return ValidationResult.failure( - "Invalid Notification type CloudEvent sink [${sink}]. ${checkSink.getMessage()}" - ) - } - return ValidationResult.success() - } -} - -/** - * Implements Validations for a CloudEvent for RPC Request. - */ -object Request : CloudEventValidator() { - override val typeName: String = "req" - override val className: String = "Request" - - override fun validateSource(cloudEvent: CloudEvent): ValidationResult { - val source = cloudEvent.source.toString().toUri() - val checkSource: ValidationResult = source.validateRpcTopicUri() - return if (checkSource.isFailure()) { - ValidationResult.failure( - "Invalid RPC Request CloudEvent source [${source}]. ${checkSource.getMessage()}" - ) - } else ValidationResult.success() - } - - - override fun validateSink(cloudEvent: CloudEvent): ValidationResult { - val sink = UCloudEvent.getSink(cloudEvent) - ?: return ValidationResult.failure("Invalid RPC Request CloudEvent sink. Request CloudEvent sink must be uri for the method to be called.") - val checkSink: ValidationResult = sink.validateRpcMethod() - if (checkSink.isFailure()) { - return ValidationResult.failure("Invalid RPC Request CloudEvent sink [${sink}]. ${checkSink.getMessage()}") - } - return ValidationResult.success() - } -} - -/** - * Implements Validations for a CloudEvent for RPC Response. - */ -object Response : CloudEventValidator() { - override val typeName: String = "res" - override val className: String = "Response" - - override fun validateSource(cloudEvent: CloudEvent): ValidationResult { - val source = cloudEvent.source.toString().toUri() - val checkSource: ValidationResult = source.validateRpcMethod() - return if (checkSource.isFailure()) { - ValidationResult.failure( - "Invalid RPC Response CloudEvent source [$source]. ${checkSource.getMessage()}" - ) - } else ValidationResult.success() - } - - override fun validateSink(cloudEvent: CloudEvent): ValidationResult { - val sink = UCloudEvent.getSink(cloudEvent) - ?: return ValidationResult.failure("Invalid CloudEvent sink. Response CloudEvent sink must be uri the destination of the response.") - val checkSink: ValidationResult = sink.validateRpcTopicUri() - if (checkSink.isFailure()) { - return ValidationResult.failure( - "Invalid RPC Response CloudEvent sink [$sink]. ${checkSink.getMessage()}" - ) - } - return ValidationResult.success() - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/rpc/README.adoc b/src/main/kotlin/org/eclipse/uprotocol/rpc/README.adoc deleted file mode 100644 index eb3a6cb..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/rpc/README.adoc +++ /dev/null @@ -1,8 +0,0 @@ -= uProtocol Rpc Interfaces -:toc: -:sectnums: - -== Overview - -The following module declares the https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/up-l2/rpcclient.adoc[RpcClient interface] defined in uProtocol specification. The interface is used by code generators to build client and service stubs for uServices. - diff --git a/src/main/kotlin/org/eclipse/uprotocol/rpc/RpcClient.kt b/src/main/kotlin/org/eclipse/uprotocol/rpc/RpcClient.kt deleted file mode 100644 index c39e617..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/rpc/RpcClient.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.rpc - -import kotlinx.coroutines.flow.Flow -import org.eclipse.uprotocol.v1.CallOptions -import org.eclipse.uprotocol.v1.UPayload -import org.eclipse.uprotocol.v1.UMessage -import org.eclipse.uprotocol.v1.UUri - -/** - * RpcClient is an interface used by code generators for uProtocol services defined in proto files such as - * the core uProtocol services found in https://github.com/eclipse-uprotocol/uprotocol-core-api. the interface - * provides a clean contract for mapping a RPC request to a response. Each - * platform MUST implement this interface. For more details please refer to - * https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/up-l2/README.adoc[RpcClient Specifications] - */ -interface RpcClient { - /** - * API for clients to invoke a method (send an RPC request) and receive the response (the returned - * {@link CompletionStage} {@link UPayload}.
- * Client will set method to be the URI of the method they want to invoke, - * payload to the request message, and attributes with the various metadata for the - * method invocation. - * @param methodUri The method URI to be invoked, ex (long form): /example.hello_world/1/rpc.SayHello. - * @param requestPayload The request message to be sent to the server. - * @param options RPC method invocation call options, see {@link CallOptions} - * @return Returns the Flow with the response message or exception with the failure - * reason as {@link UStatus}. - */ - fun invokeMethod(methodUri: UUri, requestPayload: UPayload, options: CallOptions): Flow -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/rpc/RpcMapper.kt b/src/main/kotlin/org/eclipse/uprotocol/rpc/RpcMapper.kt deleted file mode 100644 index f5e8008..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/rpc/RpcMapper.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.rpc - -import com.google.protobuf.Any -import com.google.protobuf.InvalidProtocolBufferException -import com.google.protobuf.Message -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.map -import org.eclipse.uprotocol.v1.UCode -import org.eclipse.uprotocol.v1.UMessage -import org.eclipse.uprotocol.v1.UStatus -import java.util.concurrent.CompletionException - -/** - * Inline function to map Flow<UMessage> from Link into a Flow containing the declared expected return type - * of the RPC method or throw an exception. - * @return Returns Flow containing the declared expected return type of the RPC method. - * @param The declared expected return type of the RPC method. - */ -inline fun Flow.toResponse(): Flow { - return catch { exception -> - throw CompletionException(exception.message, exception) - }.map { message -> - if (!message.hasPayload()) { - throw RuntimeException("Server returned a null payload. Expected [${T::class.java.name}]") - } - try { - val any = Any.parseFrom(message.payload.value) - if (any.`is`(T::class.java)) { - any.unpack(T::class.java) - } else { - throw RuntimeException("Unknown payload type [${any.typeUrl}]. Expected [${T::class.java.name}]") - } - } catch (e: InvalidProtocolBufferException) { - throw RuntimeException("${e.message} [${UStatus::class.java.name}]", e) - } - } -} - -/** - * Map a response of Flow<Any> from Link into a Flow containing a Result containing - * the declared expected return type T. - * @return Returns a Flow containing an Result containing the declared expected return type T, if T is UStatus - * and has code not equals to OK, failure Result will be emitted. - * @param The declared expected return type of the RPC method. - */ -inline fun Flow.toResult(): Flow> { - return toResponse().map { response -> - response.runCatching { - if (this is UStatus && code != UCode.OK) { - throw IllegalStateException("${message}, UStatus: $code") - } else { - this - } - } - }.catch { - emit(Result.failure(it)) - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/transport/README.adoc b/src/main/kotlin/org/eclipse/uprotocol/transport/README.adoc index e154d5d..54fbf83 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/transport/README.adoc +++ b/src/main/kotlin/org/eclipse/uprotocol/transport/README.adoc @@ -4,4 +4,36 @@ :source-highlighter: prettify == Overview -The purpose of this moduel is to provide the Java implementation of https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/up-l1/README.adoc[uTransport API & Data Model]. The transport API is used by all uE developers to send and receive messages across any transport. The interface is to be implemented by communication transport developers (i.e. developing a uTransport for SOME/IP, DDS, Zenoh, MQTT, etc...). +The following section implements https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/up-l1/README.adoc[uP-L1 Transport Layer Specifications]. The purpose of the transport layer is to wrap communication middlewares into a common interface that allows us to send and receive any kind of uProtocol messages (publish, request, response, notification) over any kind of transport (HTTP, MQTT, WebSockets, etc). + +the datamodel is declared in the uProtocol specifications project in https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/up-l1/README.adoc[up-core-api] folder and this project declares the language specific transport interface (UListener & UTransport) and the builders, serializers, and validators, for the up-core-api data model. . + +Below are the list of the classes and interfaces that are part of the uProtocol Transport Interface & Data Model: + +.Transport Interfaces +[table-transport-interfaces, cols="1,3"] +|=== +| Class/Interface | Description + +| xref:UTransport.kt[*`UTransport`*] +| Interface that defines the methods that a transport middleware must implement in order to be used by the uProtocol library. + +| xref:UListener.kt[*`UListener`*] +| Callback/listener interface to be able to receive messages from a transport. + +| xref:UMessageKtExt.kt[*`UMessageKtExt`*] +| Kotlin Dsl to build request, response, publish and notification UMessage, and set functions to set attributes. + +| xref:validator/UAttributesValidator.kt[*`UAttributesValidator`*] +| uProtocol Attributes validator that ensures that the publish, notification, request, and response messages are built with the correct information. + +|=== + +== Examples + +In the following section we will provide an example for how a transport implementations are expected to implement the uTransport library + +[source,java] +---- +// include::../../../../../../test/java/org/eclipse/uprotocol/communication/TestUTransport.java[] +---- \ No newline at end of file diff --git a/src/main/kotlin/org/eclipse/uprotocol/v1/UAttributesKtExt.kt b/src/main/kotlin/org/eclipse/uprotocol/transport/UAttributesKtExt.kt similarity index 64% rename from src/main/kotlin/org/eclipse/uprotocol/v1/UAttributesKtExt.kt rename to src/main/kotlin/org/eclipse/uprotocol/transport/UAttributesKtExt.kt index 0a84a73..dcec8a6 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/v1/UAttributesKtExt.kt +++ b/src/main/kotlin/org/eclipse/uprotocol/transport/UAttributesKtExt.kt @@ -1,39 +1,30 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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-FileType: SOURCE - * SPDX-FileCopyrightText: 2024 General Motors GTO LLC * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.uprotocol.v1 +package org.eclipse.uprotocol.transport import org.eclipse.uprotocol.uuid.factory.UUIDV8 +import org.eclipse.uprotocol.v1.* + /** - * Construct a UAttributesBuilder for a publish message. + * Construct a UMessageBuilder for a Publish message. * @param source Source address of the message. * @param priority The priority of the message. * @return Returns the UAttributesBuilder with the configured source and priority. */ @JvmSynthetic -fun UAttributesKt.Dsl.forPublication(source: UUri, priority: UPriority) { +internal fun UAttributesKt.Dsl.forPublication(source: UUri, priority: UPriority) { this@forPublication.source = source this@forPublication.priority = priority id = UUIDV8() @@ -48,7 +39,7 @@ fun UAttributesKt.Dsl.forPublication(source: UUri, priority: UPriority) { * @return Returns the UAttributesBuilder with the configured source, sink and priority. */ @JvmSynthetic -fun UAttributesKt.Dsl.forNotification(source: UUri, sink: UUri, priority: UPriority) { +internal fun UAttributesKt.Dsl.forNotification(source: UUri, sink: UUri, priority: UPriority) { this@forNotification.source = source this@forNotification.sink = sink this@forNotification.priority = priority @@ -65,7 +56,7 @@ fun UAttributesKt.Dsl.forNotification(source: UUri, sink: UUri, priority: UPrior * @return Returns the UAttributesBuilder with the configured source, sink, priority and ttl. */ @JvmSynthetic -fun UAttributesKt.Dsl.forRequest(source: UUri, sink: UUri, priority: UPriority, ttl: Int) { +internal fun UAttributesKt.Dsl.forRequest(source: UUri, sink: UUri, priority: UPriority, ttl: Int) { this@forRequest.source = source this@forRequest.sink = sink this@forRequest.priority = priority @@ -83,7 +74,7 @@ fun UAttributesKt.Dsl.forRequest(source: UUri, sink: UUri, priority: UPriority, * @return Returns the UAttributesBuilder with the configured source, sink, priority and reqid. */ @JvmSynthetic -fun UAttributesKt.Dsl.forResponse(source: UUri, sink: UUri, priority: UPriority, reqId: UUID) { +internal fun UAttributesKt.Dsl.forResponse(source: UUri, sink: UUri, priority: UPriority, reqId: UUID) { this@forResponse.source = source this@forResponse.sink = sink this@forResponse.priority = priority @@ -98,7 +89,7 @@ fun UAttributesKt.Dsl.forResponse(source: UUri, sink: UUri, priority: UPriority, * @return Returns the UAttributesBuilder with the configured source, sink, priority and reqid. */ @JvmSynthetic -fun UAttributesKt.Dsl.forResponse(request: UAttributes) { +internal fun UAttributesKt.Dsl.forResponse(request: UAttributes) { source = request.sink sink = request.source priority = request.priority diff --git a/src/main/kotlin/org/eclipse/uprotocol/transport/UListener.kt b/src/main/kotlin/org/eclipse/uprotocol/transport/UListener.kt index 12132ea..f68c074 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/transport/UListener.kt +++ b/src/main/kotlin/org/eclipse/uprotocol/transport/UListener.kt @@ -1,24 +1,13 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC * SPDX-License-Identifier: Apache-2.0 */ @@ -28,11 +17,13 @@ import org.eclipse.uprotocol.v1.UMessage /** - * For any implementation that defines some kind of callback or function that will be called to handle incoming messages. + * For any implementation that defines some kind of callback or function that + * will be called to handle incoming messages. */ interface UListener { /** * Method called to handle/process messages. + * * @param message Message received. * @return Returns an Ack every time a message is received and processed. */ diff --git a/src/main/kotlin/org/eclipse/uprotocol/transport/UMessageKtExt.kt b/src/main/kotlin/org/eclipse/uprotocol/transport/UMessageKtExt.kt new file mode 100644 index 0000000..648d473 --- /dev/null +++ b/src/main/kotlin/org/eclipse/uprotocol/transport/UMessageKtExt.kt @@ -0,0 +1,207 @@ +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.uprotocol.transport + +import com.google.protobuf.Any +import com.google.protobuf.ByteString +import com.google.protobuf.Message +import org.eclipse.uprotocol.v1.* + +/** + * Set the Priority of a UMessage. The priority should be at least CS4 for request and response messages, + * and CS1 for other messages, incorrect priority will be ignored. + * @param priority The priority of the message. + * + * @return Returns the UMessage with the configured priority. + */ +@JvmSynthetic +fun UMessageKt.Dsl.setPriority(priority: UPriority) { + val basePriority = when (attributes.type) { + UMessageType.UMESSAGE_TYPE_REQUEST, + UMessageType.UMESSAGE_TYPE_RESPONSE -> UPriority.UPRIORITY_CS4 + else -> UPriority.UPRIORITY_CS1 + } + if (priority.number>= basePriority.number){ + attributes = attributes.copy { this.priority = priority } + } +} + +/** + * Set the Permission Level of a UMessage. + * @param permissionLevel The permission level of the message. + * + * @return Returns the UMessage with the configured Permission Level. + */ +@JvmSynthetic +fun UMessageKt.Dsl.setPermissionLevel(permissionLevel: Int) { + attributes = attributes.copy { this.permissionLevel = permissionLevel } +} + +/** + * Set the ttl of a UMessage. + * @param ttl The time-to-live of the message. + * + * @return Returns the UMessage with the configured ttl. + */ +@JvmSynthetic +fun UMessageKt.Dsl.setTtl(ttl: Int) { + attributes = attributes.copy { this.ttl = ttl } +} + +/** + * Set the commstatus of a UMessage. + * @param commstatus The Communication Status of the message. + * + * @return Returns the UMessage with the configured commstatus. + */ +@JvmSynthetic +fun UMessageKt.Dsl.setCommStatus(commstatus: UCode) { + attributes = attributes.copy { this.commstatus = commstatus } +} + +/** + * Set the reqid of a UMessage. + * @param reqid The Required ID of the message. + * + * @return Returns the UMessage with the configured reqid. + */ +@JvmSynthetic +fun UMessageKt.Dsl.setReqid(reqid: UUID) { + attributes = attributes.copy { this.reqid = reqid } +} + +/** + * Set the token of a UMessage. + * @param token The token of the message. + * + * @return Returns the UMessage with the configured token. + */ +@JvmSynthetic +fun UMessageKt.Dsl.setToken(token: String) { + attributes = attributes.copy { this.token = token } +} + +/** + * Set the traceparent of a UMessage. + * @param traceparent The traceparent of the message. + * + * @return Returns the UMessage with the configured traceparent. + */ +@JvmSynthetic +fun UMessageKt.Dsl.setTraceparent(traceparent: String) { + attributes = attributes.copy { this.traceparent = traceparent } +} + +/** + * Set payload and payload format for a UMessage. + * @param message Google protobuf message to be packed into the payload + * + * @return Returns the UMessage with the configured payload. + */ +@JvmSynthetic +fun UMessageKt.Dsl.setPayload(message: Message) { + payload = message.toByteString() + attributes = attributes.copy { payloadFormat = UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF } +} + +/** + * Set payload and payload format for a UMessage. + * @param any Google protobuf Any message to be packed into the payload + * + * @return Returns the UMessage with the configured payload. + */ +@JvmSynthetic +fun UMessageKt.Dsl.setPayload(any: Any) { + payload = any.toByteString() + attributes = attributes.copy { payloadFormat = UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY } +} + +/** + * Set payload and payload format for a UMessage. + * @param format The format of the payload. + * @param payload The payload of the message. + * + * @return Returns the UMessage with the configured payload. + */ +@JvmSynthetic +fun UMessageKt.Dsl.setPayload(format: UPayloadFormat, payload: ByteString) { + this.payload = payload + attributes = attributes.copy { payloadFormat = format } +} + +/** + * Construct a UMessageBuilder for a publish message. + * @param source The topic the message is published to (a.k.a. Source address). + * + * @return Returns the UMessage with the configured payload. + */ +@JvmSynthetic +fun UMessageKt.Dsl.forPublication(source: UUri) { + attributes = uAttributes { + forPublication(source, UPriority.UPRIORITY_CS1) + } +} + +/** + * Construct a UMessageBuilder for a notification message. + * @param source The topic the message is published to (a.k.a. Source address). + * @param sink The destination of the client that send the request. + * @return Returns the UMessage with the configured payload. + */ +@JvmSynthetic +fun UMessageKt.Dsl.forNotification(source: UUri, sink: UUri) { + attributes = uAttributes { + forNotification(source, sink, UPriority.UPRIORITY_CS1) + } +} + +/** + * Construct a UMessageBuilder for a request message. + * @param source Source address for the message (address of the client sending the request message). + * @param sink The method that is being requested (a.k.a. destination address). + * @param ttl The time to live in milliseconds. + * @return Returns the UMessage with the configured payload. + */ +@JvmSynthetic +fun UMessageKt.Dsl.forRequest(source: UUri, sink: UUri, ttl: Int) { + attributes = uAttributes { + forRequest(source, sink, UPriority.UPRIORITY_CS4, ttl) + } +} + +/** + * Construct a UMessageBuilder for a response message. + * @param source The source address of the method that was requested + * @param sink The destination of the client that send the request. + * @param reqId The original request UUID used to correlate the response to the request. + * @return Returns the UMessage with the configured payload. + */ +@JvmSynthetic +fun UMessageKt.Dsl.forResponse(source: UUri, sink: UUri, reqId: UUID) { + attributes = uAttributes { + forResponse(source, sink, UPriority.UPRIORITY_CS4, reqId) + } +} + +/** + * Construct a UMessageBuilder for a response message. + * @param request The request message that was received. + * @return Returns the UMessage with the configured payload. + */ +@JvmSynthetic +fun UMessageKt.Dsl.forResponse(request: UAttributes) { + attributes = uAttributes { + forResponse(request) + } +} diff --git a/src/main/kotlin/org/eclipse/uprotocol/transport/UPayloadExt.kt b/src/main/kotlin/org/eclipse/uprotocol/transport/UPayloadExt.kt deleted file mode 100644 index 637183c..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/transport/UPayloadExt.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ -package org.eclipse.uprotocol.transport - -import com.google.protobuf.Any -import com.google.protobuf.Internal -import com.google.protobuf.InvalidProtocolBufferException -import com.google.protobuf.Message -import org.eclipse.uprotocol.v1.UPayload -import org.eclipse.uprotocol.v1.UPayloadFormat - - -/** - * Unpack a uPayload into a google.protobuf.Message. - * - * @param payload the payload to unpack - * @param clazz the class of the message to unpack - * @return the unpacked message - */ -@Suppress("UNCHECKED_CAST") -fun unpack(payload: UPayload, clazz: Class): T? { - if (!payload.hasValue()) { - return null - } - return try { - when (payload.format) { - UPayloadFormat.UPAYLOAD_FORMAT_UNSPECIFIED, UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY -> { - Any.parseFrom(payload.value).unpack(clazz) - } - - UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF -> { - val defaultInstance = Internal.getDefaultInstance(clazz) - defaultInstance.parserForType.parseFrom(payload.value) as T - } - - else -> null - } - } catch (e: InvalidProtocolBufferException) { - null - } -} - -/** - * Inline function to unpack a uPayload into a google.protobuf.Message. - * - * @return the unpacked message - */ -inline fun UPayload.unpack(): T? { - return unpack(this, T::class.java) -} - diff --git a/src/main/kotlin/org/eclipse/uprotocol/transport/UTransport.kt b/src/main/kotlin/org/eclipse/uprotocol/transport/UTransport.kt index 6804169..2f81125 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/transport/UTransport.kt +++ b/src/main/kotlin/org/eclipse/uprotocol/transport/UTransport.kt @@ -1,24 +1,13 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC * SPDX-License-Identifier: Apache-2.0 */ @@ -37,7 +26,6 @@ import org.eclipse.uprotocol.v1.UUri * https://github.com/eclipse-uprotocol/up-spec/blob/main/up-l1/README.adoc. */ interface UTransport { - /** * Send a message over the transport. * @param message the [UMessage] to be sent. @@ -46,23 +34,34 @@ interface UTransport { fun send(message: UMessage): UStatus /** - * Register `UListener` for `UUri` topic to be called when a message is received. - * @param topic `UUri` to listen for messages from. - * @param listener The `UListener` that will be executed when the message is + * Register `UListener` for `UUri` source and sink filters to be called when a message is received. + * + * @param sourceFilter The UAttributes::source address pattern that the message to receive needs to match. + * @param sinkFilter The UAttributes::sink address pattern that the message to receive needs to match. + * @param listener The [UListener] that will be executed when the message is * received on the given `UUri`. * @return Returns [UStatus] with [UCode.OK] if the listener is registered - * correctly, otherwise it returns with dthe appropriate failure. + * correctly, otherwise it returns with the appropriate failure. */ - fun registerListener(topic: UUri, listener: UListener): UStatus + fun registerListener(sourceFilter: UUri, sinkFilter: UUri? = null, listener: UListener): UStatus /** - * Unregister `UListener` for `UUri` topic. Messages arriving on this topic will + * Unregister `UListener` for `UUri` source and sink filters. Messages arriving on this topic will * no longer be processed by this listener. - * @param topic `UUri` to the listener was registered for. - * @param listener The `UListener` that will no longer want to be registered to receive + * + * @param sourceFilter The UAttributes::source address pattern that the message to receive needs to match. + * @param sinkFilter The UAttributes::sink address pattern that the message to receive needs to match. + * @param listener The [UListener] that will no longer want to be registered to receive * messages. * @return Returns [UStatus] with [UCode.OK] if the listener is unregistered * correctly, otherwise it returns with the appropriate failure. */ - fun unregisterListener(topic: UUri, listener: UListener): UStatus + fun unregisterListener(sourceFilter: UUri, sinkFilter: UUri? = null, listener: UListener): UStatus + + /** + * Return the source address for the uE (authority, entity, and resource information) + * + * @return [UUri] containing the source address + */ + fun getSource(): UUri } \ No newline at end of file diff --git a/src/main/kotlin/org/eclipse/uprotocol/transport/validate/UAttributesValidator.kt b/src/main/kotlin/org/eclipse/uprotocol/transport/validator/UAttributesValidator.kt similarity index 76% rename from src/main/kotlin/org/eclipse/uprotocol/transport/validate/UAttributesValidator.kt rename to src/main/kotlin/org/eclipse/uprotocol/transport/validator/UAttributesValidator.kt index e168cc6..916ba20 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/transport/validate/UAttributesValidator.kt +++ b/src/main/kotlin/org/eclipse/uprotocol/transport/validator/UAttributesValidator.kt @@ -1,32 +1,21 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.uprotocol.transport.validate +package org.eclipse.uprotocol.transport.validator -import org.eclipse.uprotocol.uri.validator.validate -import org.eclipse.uprotocol.uri.validator.validateRpcMethod -import org.eclipse.uprotocol.uri.validator.validateRpcResponse +import org.eclipse.uprotocol.uri.validator.isDefaultResourceId +import org.eclipse.uprotocol.uri.validator.isRpcMethod +import org.eclipse.uprotocol.uri.validator.isRpcResponse import org.eclipse.uprotocol.uuid.factory.getTime import org.eclipse.uprotocol.uuid.factory.isUuid import org.eclipse.uprotocol.v1.* @@ -34,12 +23,15 @@ import org.eclipse.uprotocol.validation.ValidationResult /** - * [UAttributes] is the class that defines the Payload. It is the place for configuring time to live, priority, + * [UAttributes] is the class that defines the Payload. It is the place + * for configuring time to live, priority, * security tokens and more. - * Each UAttributes class defines a different type of message payload. The payload can represent a simple published + * Each UAttributes class defines a different type of message payload. The + * payload can represent a simple published * payload with some state change, * Payload representing an RPC request or Payload representing an RPC response. - * UAttributesValidator is a base class for all UAttribute validators, that can help validate that the + * UAttributesValidator is a base class for all UAttribute validators, that can + * help validate that the * [UAttributes] object is correctly defined * to define the Payload correctly. */ @@ -47,6 +39,7 @@ sealed class UAttributesValidator { //hardcode to prevent obfuscation protected abstract val className: String protected abstract val type: UMessageType + /** * Take a [UAttributes] object and run validations. * @@ -59,7 +52,7 @@ sealed class UAttributesValidator { val errorMessage = listOf( validateType(attributes), validateTtl(attributes), validateSink(attributes), validatePriority(attributes), - validatePermissionLevel(attributes), validateReqId(attributes) + validatePermissionLevel(attributes), validateReqId(attributes), validateId(attributes) ).filter { it.isFailure() }.joinToString(",") { obj -> obj.getMessage() } @@ -104,19 +97,12 @@ sealed class UAttributesValidator { } /** - * Validate the sink UriPart for the default case. If the UAttributes does not contain a sink then the - * ValidationResult is ok. + * Validate the sink UriPart. * * @param attributes UAttributes object containing the sink to validate. * @return Returns a [ValidationResult] that is success or failed with a failure message. */ - open fun validateSink(attributes: UAttributes): ValidationResult { - return if (attributes.hasSink()) { - attributes.sink.validate() - } else { - ValidationResult.success() - } - } + abstract fun validateSink(attributes: UAttributes): ValidationResult /** * Validate the permissionLevel for the default case. If the UAttributes does not contain a permission level then @@ -134,15 +120,15 @@ sealed class UAttributesValidator { } /** - * Validate the correlationId for the default case. If the UAttributes does not contain a request id then the - * ValidationResult is ok. + * Validate the correlationId for the default case. Only the response message should have a reqid. * * @param attributes Attributes object containing the request id to validate. - * @return Returns a [ValidationResult] that is success or failed with a failure message. + * @return Returns a [ValidationResult] that is success or failed with a + * failure message. */ open fun validateReqId(attributes: UAttributes): ValidationResult { - return if (attributes.hasReqid() && !attributes.reqid.isUuid()) { - ValidationResult.failure("Invalid UUID") + return if (attributes.hasReqid()) { + ValidationResult.failure("Message should not have a reqid") } else { ValidationResult.success() } @@ -155,13 +141,32 @@ sealed class UAttributesValidator { * @return Returns a [ValidationResult] that is success or failed with a failure message. */ open fun validatePriority(attributes: UAttributes): ValidationResult { - return if (attributes.priority.number >= UPriority.UPRIORITY_CS0_VALUE) { + return if (attributes.priority.number >= UPriority.UPRIORITY_CS1_VALUE) { ValidationResult.success() } else { ValidationResult.failure("Invalid UPriority [${attributes.priority.name}]") } } + /** + * Validate the Id for the default case. If the UAttributes object does not + * contain an Id, + * the ValidationResult is failed. + * + * @param attributes Attributes object containing the id to validate. + * @return Returns a [ValidationResult] that is success or failed with a + * failure message. + */ + fun validateId(attributes: UAttributes): ValidationResult { + return when { + !attributes.hasId() -> ValidationResult.failure("Missing id") + !attributes.id.isUuid() -> { + ValidationResult.failure("Attributes must contain valid uProtocol UUID in id property") + } + + else -> ValidationResult.success() + } + } /** * Validate the [UMessageType] attribute, it is required. @@ -169,7 +174,7 @@ sealed class UAttributesValidator { * @param attributes UAttributes object containing the message type to validate. * @return Returns a [ValidationResult] that is success or failed with a failure message. */ - fun validateType(attributes: UAttributes): ValidationResult{ + fun validateType(attributes: UAttributes): ValidationResult { return if (type == attributes.type) { ValidationResult.success() } else { @@ -206,12 +211,24 @@ sealed class UAttributesValidator { object Publish : UAttributesValidator() { override val className: String = "Publish" override val type: UMessageType = UMessageType.UMESSAGE_TYPE_PUBLISH + + /** + * Validate the sink UriPart for Publish events. Publish should not have a sink. + * + * @param attributes UAttributes object containing the sink to validate. + * @return Returns a [ValidationResult] that is success or failed with a + * failure message. + */ + override fun validateSink(attributes: UAttributes): ValidationResult { + return if (attributes.hasSink()) ValidationResult.failure("Sink should not be present") + else ValidationResult.success() + } } /** * Implements validations for UAttributes that define a message that is meant for notifications. */ -object Notification: UAttributesValidator(){ +object Notification : UAttributesValidator() { override val className: String = "Notification" override val type: UMessageType = UMessageType.UMESSAGE_TYPE_NOTIFICATION @@ -223,12 +240,16 @@ object Notification: UAttributesValidator(){ * @return Returns a [ValidationResult] that is success or failed with a failure message. */ override fun validateSink(attributes: UAttributes): ValidationResult { - if (!attributes.hasSink() || attributes.sink == UUri.getDefaultInstance()) { - return ValidationResult.failure("Missing Sink") + return when { + !attributes.hasSink() || attributes.sink === UUri.getDefaultInstance() -> + ValidationResult.failure("Missing Sink") + !attributes.sink.isDefaultResourceId() -> ValidationResult.failure("Invalid Sink Uri") + else -> ValidationResult.success() } - return ValidationResult.success() + } } + /** * Implements validations for UAttributes that define a message that is meant for an RPC request. */ @@ -244,10 +265,10 @@ object Request : UAttributesValidator() { * @return Returns a [ValidationResult] that is success or failed with a failure message. */ override fun validateSink(attributes: UAttributes): ValidationResult { - return if (!attributes.hasSink()) { - ValidationResult.failure("Missing Sink") - } else { - attributes.sink.validateRpcMethod() + return when { + !attributes.hasSink() -> ValidationResult.failure("Missing Sink") + !attributes.sink.isRpcMethod() -> ValidationResult.failure("Invalid Sink Uri") + else -> ValidationResult.success() } } @@ -290,6 +311,7 @@ object Request : UAttributesValidator() { object Response : UAttributesValidator() { override val className: String = "Response" override val type: UMessageType = UMessageType.UMESSAGE_TYPE_RESPONSE + /** * Validates that attributes for a message meant for an RPC response has a destination sink. * In the case of an RPC response, the sink is required. @@ -298,10 +320,14 @@ object Response : UAttributesValidator() { * @return Returns a [ValidationResult] that is success or failed with a failure message. */ override fun validateSink(attributes: UAttributes): ValidationResult { - if (!attributes.hasSink() || attributes.sink === UUri.getDefaultInstance()) { - return ValidationResult.failure("Missing Sink") + return when { + !attributes.hasSink() || attributes.sink === UUri.getDefaultInstance() -> + ValidationResult.failure("Missing Sink") + + !attributes.sink.isRpcResponse() -> ValidationResult.failure("Invalid Sink Uri") + else -> ValidationResult.success() } - return attributes.sink.validateRpcResponse() + } /** @@ -315,7 +341,7 @@ object Response : UAttributesValidator() { return ValidationResult.failure("Missing correlationId") } return if (!attributes.reqid.isUuid()) { - ValidationResult.failure("Invalid correlationId [${attributes.reqid}]") + ValidationResult.failure("Invalid correlation UUID") } else { ValidationResult.success() } diff --git a/src/main/kotlin/org/eclipse/uprotocol/uri/README.adoc b/src/main/kotlin/org/eclipse/uprotocol/uri/README.adoc index e30f446..49f1cb1 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/uri/README.adoc +++ b/src/main/kotlin/org/eclipse/uprotocol/uri/README.adoc @@ -5,36 +5,12 @@ == Overview -The following folder contains the everything but the data model for UUri (builders, serializers, validators, etc...) per https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/basics/uri.adoc[uProtocol URI Specifications]. -The data model is defined in https://github.com/eclipse-uprotocol/uprotocol-core-api/blob/main/src/main/proto/uri.proto[uri.proto] and included as a dependency for this project. - -IMPORTANT: For more details about the data model, various formats (object, long, micro) and their uses, please refer to https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/basics/uri.adoc[uProtocol URI Specifications]. +location of the uProtocol URI Factory serializers, validators, and builders per https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/basics/uri.adoc[uProtocol URI Specifications]. == Using the SDK -When building UUri, you can choose to populate it with only names, only numbers, or both (resolved). When you should use each is described the best practice section of https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/basics/uri.adoc[uProtocol URI Specifications]. - -=== Building an RPC Method -[,kotlin] ----- -val uri = uUri { - authority = uAuthority { - name = "MyDevice" - ip = ByteString.copyFrom(InetAddress.getByName("192.168.1.100").address) - } - entity = uEntity { - name = "Service" - id = 10203 - versionMajor = 1 - } - resource = UResourceFactory.createRpcRequest("Raise", 10) - } ----- - -=== Validating -[,kotlin] +[source,java] ---- - val status:ValidationResult = uuri.validateRpcMethod() - assertTrue(status.isSuccess()) +include::../../../../../../test/kotlin/org/eclipse/uprotocol/uri/UUriExamples.kt[] ---- diff --git a/src/main/kotlin/org/eclipse/uprotocol/uri/UUriConstant.kt b/src/main/kotlin/org/eclipse/uprotocol/uri/UUriConstant.kt new file mode 100644 index 0000000..b79f62f --- /dev/null +++ b/src/main/kotlin/org/eclipse/uprotocol/uri/UUriConstant.kt @@ -0,0 +1,36 @@ +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.uprotocol.uri + +object UUriConstant { + /** + * The minimum publish/notification topic id for a URI. + */ + const val MIN_TOPIC_ID = 0x8000 + + /** + * The Default resource id. + */ + const val DEFAULT_RESOURCE_ID = 0 + + /** + * major version wildcard + */ + const val MAJOR_VERSION_WILDCARD = 0xFF + + /** + * The wildcard id for a field. + */ + const val WILDCARD_ID: Int = 0xFFFF +} \ No newline at end of file diff --git a/src/main/kotlin/org/eclipse/uprotocol/uri/Uri.kt b/src/main/kotlin/org/eclipse/uprotocol/uri/Uri.kt deleted file mode 100644 index 6d0385b..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/uri/Uri.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2024 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.uri - -@JvmInline -value class Uri(val value: String=""){ - override fun toString(): String { - return value - } -} - -fun String.toUri(): Uri = Uri(this) \ No newline at end of file diff --git a/src/main/kotlin/org/eclipse/uprotocol/uri/factory/UEntityFactory.kt b/src/main/kotlin/org/eclipse/uprotocol/uri/factory/UEntityFactory.kt deleted file mode 100644 index a51cebe..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/uri/factory/UEntityFactory.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2024 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.uri.factory - -import com.google.protobuf.Descriptors.ServiceDescriptor -import org.eclipse.uprotocol.UprotocolOptions -import org.eclipse.uprotocol.v1.UEntity -import org.eclipse.uprotocol.v1.uEntity - - -/** - * Create UEntity to/from proto information - */ -object UEntityFactory { - /** - * Builds a UEntity for a protobuf generated code Service Descriptor. - * @param descriptor The protobuf generated code Service Descriptor. - * @return Returns a UEntity for a protobuf generated code Service Descriptor. - */ - fun fromProto(descriptor: ServiceDescriptor): UEntity { - return uEntity { - descriptor.options.getExtension(UprotocolOptions.name)?.let{ name = it } - descriptor.options.getExtension(UprotocolOptions.id)?.let{ id = it} - descriptor.options.getExtension(UprotocolOptions.versionMajor)?.let{versionMajor = it} - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/org/eclipse/uprotocol/uri/factory/UUriFactory.kt b/src/main/kotlin/org/eclipse/uprotocol/uri/factory/UUriFactory.kt new file mode 100644 index 0000000..13d30e4 --- /dev/null +++ b/src/main/kotlin/org/eclipse/uprotocol/uri/factory/UUriFactory.kt @@ -0,0 +1,53 @@ +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.uprotocol.uri.factory + +import com.google.protobuf.Descriptors.ServiceDescriptor +import org.eclipse.uprotocol.Uoptions +import org.eclipse.uprotocol.v1.UUri +import org.eclipse.uprotocol.v1.uUri + +object UUriFactory { + /** + * Builds a UUri for a protobuf generated code Service Descriptor. + * + * @param descriptor The protobuf generated code Service Descriptor. + * @param resourceId The resource id. + * @param authorityName The authority name. + * @return Returns a UUri for a protobuf generated code Service Descriptor. + */ + fun fromProto(descriptor: ServiceDescriptor, resourceId: Int, authorityName: String? = null): UUri { + return uUri { + ueId = descriptor.options.getExtension(Uoptions.serviceId) + ueVersionMajor = descriptor.options.getExtension(Uoptions.serviceVersionMajor) + this.resourceId = resourceId + if (!authorityName.isNullOrEmpty()) { + this.authorityName = authorityName + } + } + } + + /** + * Builds a UUri for a protobuf generated code Service Descriptor. + * + * @return Returns a UUri for a protobuf generated code Service Descriptor. + */ + val ANY: UUri = uUri { + authorityName = "*" + ueId = 0xFFFF + ueVersionMajor = 0xFF + resourceId = 0xFFFF + + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/IpAddress.kt b/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/IpAddress.kt deleted file mode 100644 index 6cee2e6..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/IpAddress.kt +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2024 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ -package org.eclipse.uprotocol.uri.serializer - - -object IpAddress { - fun toBytes(ipAddress: String): ByteArray { - return if (ipAddress.isEmpty()) { - ByteArray(0) - } else if (isValidIPv4Address(ipAddress)) { - convertIPv4ToByteArray(ipAddress) - } else if (isValidIPv6Address(ipAddress)) { - convertIPv6ToByteArray(ipAddress) - } else { - ByteArray(0) - } - } - - fun isValid(ipAddress: String): Boolean { - return ipAddress.isNotEmpty() && (isValidIPv4Address(ipAddress) || isValidIPv6Address(ipAddress)) - } - - private fun isValidIPv4Address(ipAddress: String): Boolean { - val octetRange = "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" - val ipv4Regex = """^$octetRange\.$octetRange\.$octetRange\.$octetRange$""".toRegex() - return ipAddress.matches(ipv4Regex) - } - - private fun convertIPv4ToByteArray(ipAddress: String): ByteArray { - val octets = ipAddress.split(".") - return ByteArray(4) { i -> octets[i].toInt().toByte() } - } - - private fun isValidIPv6Address(ipAddress: String): Boolean { - // Split the address into groups using the colon separator - val groups = ipAddress.split(":".toRegex()).dropLastWhile { it.isEmpty() } - // Check the number of groups - if (groups.size > 8) { - return false // Too many groups - } - - var hasDoubleColon = false - var emptyGroups = 0 - - for (group in groups) { - // Check for an empty group - if (group.isEmpty()) { - emptyGroups++ - // Double colon can only appear once - if (emptyGroups > 1) { - return false - } - hasDoubleColon = true - } else { - // Check each character in the group - for (element in group) { - // Check if the character is a valid hexadecimal digit - if (!isValidHexDigit(element)) { - return false - } - } - } - } - - // Check if the address ends with a double colon - if (ipAddress.endsWith(":")) { - // We already had an empty group so crap out - if (emptyGroups > 0) { - return false - } - hasDoubleColon = true - } - - // Check the final number of groups - if (!hasDoubleColon && groups.size != 8) { - return false // Not enough groups - } - - return true - } - - private fun isValidHexDigit(c: Char): Boolean { - return (c in 'a'..'f') || (c in '0'..'9') || (c in 'A'..'F') - } - - private fun convertIPv6ToByteArray(ipAddress: String): ByteArray { - // Split the address into groups using the colon separator - val groups = ipAddress.split(":".toRegex()).dropLastWhile { it.isEmpty() } - // Initialize the byte array - val ipAddressBytes = ByteArray(16) - - // Index to keep track of the current position in the byte array - var index = 0 - - for (group in groups) { - // Check for an empty group - if (group.isEmpty()) { - // Calculate the number of empty groups needed - val emptyGroups = 8 - (groups.size - 1) - - // Fill the empty groups with zeros - repeat(emptyGroups * 2) { - ipAddressBytes[index++] = 0 - } - } else { - // Convert the group to a 16-bit integer - val value = group.toInt(16) - - // Split the value into two bytes - ipAddressBytes[index++] = ((value shr 8) and 0xFF).toByte() - ipAddressBytes[index++] = (value and 0xFF).toByte() - } - } - - return ipAddressBytes - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/LongUriSerializer.kt b/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/LongUriSerializer.kt deleted file mode 100644 index 1fb5d84..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/LongUriSerializer.kt +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.uri.serializer - -import org.eclipse.uprotocol.uri.validator.isEmpty -import org.eclipse.uprotocol.v1.* - -/** - * UUri Serializer that serializes a UUri to a long format string per - * https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/basics/uri.adoc - */ -class LongUriSerializer private constructor() : UriSerializer { - /** - * Support for serializing [UUri] objects into their String format. - * @param uri [UUri] object to be serialized to the String format. - * @return Returns the String format of the supplied [UUri] that can be used as a sink or a source in a uProtocol publish communication. - */ - override fun serialize(uri: UUri): String { - if (uri.isEmpty()) { - return "" - } - val sb = StringBuilder() - if (uri.hasAuthority()) { - sb.append("//") - sb.append(uri.authority.name) - } - sb.append("/") - sb.append(buildSoftwareEntityPartOfUri(uri.entity)) - sb.append(buildResourcePartOfUri(uri)) - return sb.toString().replace("/+$".toRegex(), "") - } - - /** - * Deserialize a String into a UUri object. - * @param uri A long format uProtocol URI. - * @return Returns an UUri data object. - */ - - override fun deserialize(uri: String): UUri { - if (uri.isBlank()) { - return UUri.getDefaultInstance() - } - val uuri: String = if (uri.contains(":")) uri.substring(uri.indexOf(":") + 1) else uri - .replace('\\', '/') - val isLocal: Boolean = !uuri.startsWith("//") - val uriParts: List = removeEmpty(uuri.split("/".toRegex())) - val numberOfPartsInUri = uriParts.size - if (numberOfPartsInUri == 0 || numberOfPartsInUri == 1) { - return UUri.getDefaultInstance() - } - val useName: String - var useVersion = "" - var uResource: UResource? = null - var uAuthority: UAuthority? = null - if (isLocal) { - useName = uriParts[1] - if (numberOfPartsInUri > 2) { - useVersion = uriParts[2] - if (numberOfPartsInUri > 3) { - uResource = parseFromString( - uriParts[3] - ) - } - } - } else { - // If authority is blank, it is an error - if (uriParts[2].isBlank()) { - return UUri.getDefaultInstance() - } - uAuthority = uAuthority { name = uriParts[2] } - if (uriParts.size > 3) { - useName = uriParts[3] - if (numberOfPartsInUri > 4) { - useVersion = uriParts[4] - if (numberOfPartsInUri > 5) { - uResource = parseFromString( - uriParts[5] - ) - } - } - } else { - return uUri { authority = uAuthority } - } - } - var useVersionInt: Int? = null - try { - if (useVersion.isNotBlank()) { - useVersionInt = Integer.valueOf(useVersion) - } - } catch (ignored: NumberFormatException) { - return UUri.getDefaultInstance() - } - - return uUri { - uAuthority?.let { authority = it } - uResource?.let { resource = it } - entity = uEntity { - name = useName - useVersionInt?.let { versionMajor = it } - } - - } - } - - companion object { - val INSTANCE = LongUriSerializer() - - private fun buildResourcePartOfUri(uri: UUri): String { - if (!uri.hasResource()) { - return "" - } - val uResource: UResource = uri.resource - val sb = StringBuilder("/") - sb.append(uResource.name) - if (uResource.hasInstance()) { - sb.append(".").append(uResource.instance) - } - if (uResource.hasMessage()) { - sb.append("#").append(uResource.message) - } - return sb.toString() - } - - /** - * Create the service part of the uProtocol URI from a software entity object. - * @param use Software Entity representing a service or an application. - */ - private fun buildSoftwareEntityPartOfUri(use: UEntity): String { - val sb = StringBuilder(use.name.trim()) - sb.append("/") - if (use.versionMajor > 0) { - sb.append(use.versionMajor) - } - return sb.toString() - } - - /** - * Static factory method for creating a UResource using a string that contains - * name + instance + message. - * @param resourceString String that contains the UResource information. - * @return Returns a UResource object. - */ - private fun parseFromString(resourceString: String): UResource { - val parts: List = removeEmpty(resourceString.split("#")) - val nameAndInstance = parts[0] - val nameAndInstanceParts: List = removeEmpty(nameAndInstance.split(".")) - val resourceName = nameAndInstanceParts[0] - val resourceInstance = if (nameAndInstanceParts.size > 1) nameAndInstanceParts[1] else null - val resourceMessage = if (parts.size > 1) parts[1] else null - - return uResource { - name = resourceName - resourceInstance?.let { instance = it } - resourceMessage?.let { message = it } - if (resourceName.contains("rpc") && resourceInstance != null && resourceInstance.contains("response")) { - id = 0 - } - } - } - - fun removeEmpty(parts: List): List { - val result = parts.toMutableList() - - // Iterate through the list in reverse and remove empty strings - while (result.isNotEmpty() && result.last().isEmpty()) { - result.removeAt(result.size - 1) - } - return result - - } - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/MicroUriSerializer.kt b/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/MicroUriSerializer.kt deleted file mode 100644 index f445b25..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/MicroUriSerializer.kt +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.uri.serializer - -import com.google.protobuf.ByteString -import org.eclipse.uprotocol.uri.validator.isEmpty -import org.eclipse.uprotocol.uri.validator.isMicroForm -import org.eclipse.uprotocol.v1.* -import java.io.ByteArrayOutputStream -import java.io.IOException - -/** - * UUri Serializer that serializes a UUri to a byte[] (micro format) per - * [...](https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/basics/uri.adoc) - */ -class MicroUriSerializer private constructor() : UriSerializer { - /** - * The type of address used for Micro URI. - */ - private enum class AddressType(private val value: Int) { - LOCAL(0), IPv4(1), IPv6(2), ID(3); - - fun getValue(): Byte { - return value.toByte() - } - - companion object { - fun from(value: Int): AddressType? { - return entries.toTypedArray().firstOrNull { - it.value == value - } - } - } - } - - /** - * Serialize a UUri into a byte[] following the Micro-URI specifications. - * @param uri The [UUri] data object. - * @return Returns a byte[] representing the serialized [UUri]. - */ - override fun serialize(uri: UUri): ByteArray { - if (uri.isEmpty() || !uri.isMicroForm()) { - return ByteArray(0) - } - val uEId = uri.entity.id - val uResourceId = uri.resource.id - val os = ByteArrayOutputStream() - // UP_VERSION - os.write(UP_VERSION.toInt()) - val type = if (uri.authority.hasIp()) { - val length = uri.authority.getIp().size() - when (length) { - 4 -> { - AddressType.IPv4 - } - - 16 -> { - AddressType.IPv6 - } - - else -> { - return ByteArray(0) - } - } - } else if (uri.authority.hasId()) { - AddressType.ID - } else { - AddressType.LOCAL - } - - os.write(type.getValue().toInt()) - - // URESOURCE_ID - os.write(uResourceId shr 8) - os.write(uResourceId) - - // UENTITY_ID - os.write(uEId shr 8) - os.write(uEId) - - // UE_VERSION - os.write((if (uri.entity.versionMajor == 0) 0.toByte() else uri.entity.versionMajor).toInt()) - - // UNUSED - os.write(0.toByte().toInt()) - - // Populating the UAuthority - if (type != AddressType.LOCAL) { - - // Write the ID length if the type is ID - if (type == AddressType.ID) { - os.write(uri.authority.id.size()) - } - try { - if (uri.authority.hasIp()) { - os.write(uri.authority.getIp().toByteArray()) - } else { - os.write(uri.authority.getId().toByteArray()) - } - } catch (e: IOException) { - return ByteArray(0) - } - } - return os.toByteArray() - } - - /** - * Deserialize a byte[] into a [UUri] object. - * @param uri A byte[] uProtocol micro URI. - * @return Returns an [UUri] data object from the serialized format of a microUri. - */ - override fun deserialize(uri: ByteArray): UUri { - if (uri.size < LOCAL_MICRO_URI_LENGTH) { - return UUri.getDefaultInstance() - } - - // Need to be version 1 - if (uri[0].toInt() != 0x1) { - return UUri.getDefaultInstance() - } - val uResourceId = uri[2].toInt() and 0xFF shl 8 or (uri[3].toInt() and 0xFF) - val type: AddressType = AddressType.from(uri[1].toInt()) ?: return UUri.getDefaultInstance() - - // Validate Type is found - - // Validate that the microUri is the correct length for the type - val addressType: AddressType = type - if (addressType == AddressType.LOCAL && uri.size != LOCAL_MICRO_URI_LENGTH) { - return UUri.getDefaultInstance() - } else if (addressType == AddressType.IPv4 && uri.size != IPV4_MICRO_URI_LENGTH) { - return UUri.getDefaultInstance() - } else if (addressType == AddressType.IPv6 && uri.size != IPV6_MICRO_URI_LENGTH) { - return UUri.getDefaultInstance() - } - - // UENTITY_ID - val ueId = uri[4].toInt() and 0xFF shl 8 or (uri[5].toInt() and 0xFF) - - // UE_VERSION - val uiVersion: Int = uri[6].toUByte().toInt() - - // Calculate uAuthority - var uAuthority: UAuthority? = null - when (addressType) { - - AddressType.IPv4, AddressType.IPv6 -> uAuthority = uAuthority { - ip = ByteString.copyFrom( - uri, 8, if (addressType == AddressType.IPv4) 4 else 16 - ) - } - - AddressType.ID -> { - val length: Int = uri[8].toUByte().toInt() - - uAuthority = uAuthority { - id = ByteString.copyFrom( - uri, 9, length - ) - } - } - - else -> {} - } - - return uUri { - entity = uEntity { - id = ueId - versionMajor = uiVersion - } - resource = uResource { - from(uResourceId) - } - uAuthority?.let { authority = it } - } - } - - companion object { - const val LOCAL_MICRO_URI_LENGTH = 8 // local micro URI length - const val IPV4_MICRO_URI_LENGTH = 12 // IPv4 micro URI length - const val IPV6_MICRO_URI_LENGTH = 24 // IPv6 micro UriPart length - const val UP_VERSION: Byte = 0x1 // UP version - val INSTANCE = MicroUriSerializer() - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/ShortUriSerializer.kt b/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/ShortUriSerializer.kt deleted file mode 100644 index 060ec1d..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/ShortUriSerializer.kt +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.uri.serializer - -import com.google.protobuf.ByteString -import org.eclipse.uprotocol.uri.serializer.IpAddress.isValid -import org.eclipse.uprotocol.uri.serializer.IpAddress.toBytes -import org.eclipse.uprotocol.uri.validator.isEmpty -import org.eclipse.uprotocol.v1.* -import java.net.InetAddress -import java.net.UnknownHostException - - -/** - * UUri Serializer that serializes a UUri to a Short format string per - * https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/basics/uri.adoc - */ -class ShortUriSerializer private constructor() : UriSerializer { - /** - * Support for serializing {@link UUri} objects into their String format. - * @param uri {@link UUri} object to be serialized to the String format. - * @return Returns the String format of the supplied {@link UUri} that can be used - * as a sink or a source in a uProtocol publish communication. - */ - override fun serialize(uri: UUri): String { - if (uri.isEmpty()) { - return "" - } - - val sb = StringBuilder() - - if (uri.hasAuthority()) { - val authority: UAuthority = uri.authority - if (authority.hasIp()) { - try { - sb.append("/") - sb.append(InetAddress.getByAddress(authority.ip.toByteArray())) - } catch (e: UnknownHostException) { - return "" - } - } else if (authority.hasId()) { - sb.append("//") - sb.append(authority.id.toStringUtf8()) - } else { - return "" - } - } - sb.append("/") - - sb.append(uri.entity.buildUriString()) - - sb.append(uri.buildUriString()) - - return sb.toString().replace("/+$", "") - } - - /** - * Deserialize a String into a UUri object. - * @param uri A short format uProtocol URI. - * @return Returns an UUri data object. - */ - override fun deserialize(uri: String): UUri { - if (uri.isBlank()) { - return UUri.getDefaultInstance() - } - - val uriString: String = - if (uri.contains(":")) uri.substring(uri.indexOf(":") + 1) else uri.replace('\\', '/') - - val isLocal = !uriString.startsWith("//") - - val uriParts = uriString.split("/".toRegex()).dropLastWhile { it.isEmpty() } - val numberOfPartsInUri = uriParts.size - - if (numberOfPartsInUri < 2) { - return UUri.getDefaultInstance() - } - - val uEId: String - var ueVersion = "" - - var uResource: UResource? = null - - var uAuthority: UAuthority? = null - - if (isLocal) { - uEId = uriParts[1] - if (numberOfPartsInUri > 2) { - ueVersion = uriParts[2] - - if (numberOfPartsInUri > 3) { - uResource = parseFromString(uriParts[3]) - } - // Too many parts now - if (numberOfPartsInUri > 4) { - return UUri.getDefaultInstance() - } - } - } else { - // If authority is blank, it is an error - if (uriParts[2].isBlank()) { - return UUri.getDefaultInstance() - } - - // Try if it is an IP address, if not then it must be an ID - uAuthority = uAuthority { - if (isValid(uriParts[2])) { - ip = ByteString.copyFrom(toBytes(uriParts[2])) - } else { - id = ByteString.copyFromUtf8(uriParts[2]) - } - } - - if (uriParts.size > 3) { - uEId = uriParts[3] - if (numberOfPartsInUri > 4) { - ueVersion = uriParts[4] - - if (numberOfPartsInUri > 5) { - uResource = parseFromString(uriParts[5]) - } - // Way too many parts in the URI - if (numberOfPartsInUri > 6) { - return UUri.getDefaultInstance() - } - } - } else { - return uUri { - authority = uAuthority - } - } - } - - var useVersionInt: Int? = null - var ueIdInt: Int? = null - try { - if (ueVersion.isNotBlank()) { - useVersionInt = ueVersion.toInt() - } - - if (uEId.isNotBlank()) { - ueIdInt = uEId.toInt() - } - } catch (ignored: java.lang.NumberFormatException) { - return UUri.getDefaultInstance() - } - - return uUri { - entity = uEntity { - - ueIdInt?.let { id = it } - useVersionInt?.let { versionMajor = it } - - } - uAuthority?.let { authority = it } - uResource?.let { resource = it } - } - - } - - companion object { - val INSTANCE = ShortUriSerializer() - - /** - * Static factory method for creating a UResource using a string value - * @param resourceString String that contains the UResource id. - * @return Returns a UResource object. - */ - private fun parseFromString(resourceString: String): UResource { - return try { - uResource { from(resourceString.toInt()) } - } catch (ignored: NumberFormatException) { - UResource.getDefaultInstance() - } - } - - private fun UUri.buildUriString(): String { - return if (hasResource()) { - "/${resource.id}" - } else { - "" - } - } - - /** - * Create the service part of the uProtocol URI from an software entity object. - */ - private fun UEntity.buildUriString(): String { - val versionStr = if (versionMajor > 0) { - versionMajor - } else { - "" - } - return "$id/$versionStr" - } - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/UriSerializer.kt b/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/UriSerializer.kt index 006d8a0..67fd21d 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/UriSerializer.kt +++ b/src/main/kotlin/org/eclipse/uprotocol/uri/serializer/UriSerializer.kt @@ -1,49 +1,112 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC * SPDX-License-Identifier: Apache-2.0 */ package org.eclipse.uprotocol.uri.serializer + +import org.eclipse.uprotocol.uri.UUriConstant.MAJOR_VERSION_WILDCARD +import org.eclipse.uprotocol.uri.UUriConstant.WILDCARD_ID +import org.eclipse.uprotocol.uri.validator.isEmpty import org.eclipse.uprotocol.v1.UUri + +/** + * Support for serializing [UUri] objects into their String format. + * @return Returns the String format of the supplied [UUri] that can be used as a sink or a source in a uProtocol publish communication. + */ +fun UUri.serialize(): String { + if (isEmpty()) { + return "" + } + + val sb = StringBuilder() + + if (authorityName.isNotBlank()) { + sb.append("//") + sb.append(authorityName) + } + + sb.append("/") + sb.append(Integer.toHexString(ueId)) + sb.append("/") + sb.append(Integer.toHexString(ueVersionMajor)) + sb.append("/") + sb.append(Integer.toHexString(resourceId)) + return sb.toString().replace("/+$", "") +} /** - * UUris are used in transport layers and hence need to be serialized. - * Each transport supports different serialization formats. - * For more information, please refer to [...](https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/basics/uri.adoc) - * @param The data structure that the UUri will be serialized into. For example String or byte[]. - */ -interface UriSerializer { - /** - * Deserialize from the format to a [UUri]. - * @param uri serialized UUri. - * @return Returns a [UUri] object from the serialized format from the wire. - */ - fun deserialize(uri: T): UUri - - /** - * Serialize from a [UUri] to a specific serialization format. - * @param uri UUri object to be serialized to the format T. - * @return Returns the [UUri] in the transport serialized format. - */ - fun serialize(uri: UUri): T -} \ No newline at end of file + * Deserialize from the String to a [UUri]. + * @return Returns a [UUri] object from the serialized format from the wire. + */ +fun String.deserializeAsUUri(): UUri { + if (isBlank()) { + return UUri.getDefaultInstance() + } + val uuri: String = if (contains(":")) substring(indexOf(":") + 1) else + replace('\\', '/') + val isLocal: Boolean = !uuri.startsWith("//") + val uriParts: List = uuri.split("/").dropLastWhile { it.isEmpty() } + val numberOfPartsInUri = uriParts.size + if (numberOfPartsInUri == 0 || numberOfPartsInUri == 1) { + return UUri.getDefaultInstance() + } + + // TODO: optimize the logic + val builder = UUri.newBuilder() + try { + if (isLocal) { + builder.setUeId(Integer.parseUnsignedInt(uriParts[1], 16)) + if (numberOfPartsInUri > 2) { + builder.setUeVersionMajor(Integer.parseUnsignedInt(uriParts[2], 16)) + + if (numberOfPartsInUri > 3) { + builder.setResourceId(Integer.parseUnsignedInt(uriParts[3], 16)) + } + } + } else { + // If authority is blank, it is an error + if (uriParts[2].isBlank()) { + return UUri.getDefaultInstance() + } + builder.setAuthorityName(uriParts[2]) + + if (uriParts.size > 3) { + builder.setUeId(Integer.parseUnsignedInt(uriParts[3], 16)) + if (numberOfPartsInUri > 4) { + builder.setUeVersionMajor(Integer.parseUnsignedInt(uriParts[4], 16)) + + if (numberOfPartsInUri > 5) { + builder.setResourceId(Integer.parseUnsignedInt(uriParts[5], 16)) + } + } + } + } + } catch (e: NumberFormatException) { + return UUri.getDefaultInstance() + } + + + // Ensure the major version is less than the wildcard + if (builder.ueVersionMajor > MAJOR_VERSION_WILDCARD) { + return UUri.getDefaultInstance() + } + + + // Ensure the resource id is less than the wildcard + if (builder.resourceId > WILDCARD_ID) { + return UUri.getDefaultInstance() + } + + return builder.build() +} + diff --git a/src/main/kotlin/org/eclipse/uprotocol/uri/validator/UriValidator.kt b/src/main/kotlin/org/eclipse/uprotocol/uri/validator/UriValidator.kt index 2a67761..57d4361 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/uri/validator/UriValidator.kt +++ b/src/main/kotlin/org/eclipse/uprotocol/uri/validator/UriValidator.kt @@ -1,80 +1,20 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC * SPDX-License-Identifier: Apache-2.0 */ package org.eclipse.uprotocol.uri.validator +import org.eclipse.uprotocol.uri.UUriConstant.DEFAULT_RESOURCE_ID +import org.eclipse.uprotocol.uri.UUriConstant.MIN_TOPIC_ID import org.eclipse.uprotocol.v1.* -import org.eclipse.uprotocol.validation.ValidationResult -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.contract - - -/** - * Validate a [UUri] to ensure that it has at least a name for the uEntity. - * - * @return Returns UStatus containing a success or a failure with the error message. - */ -fun UUri.validate(): ValidationResult { - if (isEmpty()) { - return ValidationResult.failure("Uri is empty.") - } - if (hasAuthority() && !authority.isRemote()) { - return ValidationResult.failure("Uri is remote missing uAuthority.") - } - return if (entity.name.isBlank()) { - ValidationResult.failure("Uri is missing uSoftware Entity name.") - } else ValidationResult.success() -} - -/** - * Validate a [UUri] that is meant to be used as an RPC method URI. Used in Request sink values and Response source values. - * - * @return Returns UStatus containing a success or a failure with the error message. - */ -fun UUri.validateRpcMethod(): ValidationResult { - val status: ValidationResult = validate() - if (status.isFailure()) { - return status - } - return if (!isRpcMethod()) { - ValidationResult.failure("Invalid RPC method uri. Uri should be the method to be called, or method from response.") - } else ValidationResult.success() -} - -/** - * Validate a [UUri] that is meant to be used as an RPC response URI. Used in Request source values and Response sink values. - * - * @return Returns UStatus containing a success or a failure with the error message. - */ -fun UUri.validateRpcResponse(): ValidationResult { - val status: ValidationResult = validate() - if (status.isFailure()) { - return status - } - return if (!isRpcResponse()) { - ValidationResult.failure("Invalid RPC response type.") - } else ValidationResult.success() -} /** * Returns true if URI is of type RPC. A UUri is of type RPC if it contains the word rpc in the resource name @@ -83,29 +23,9 @@ fun UUri.validateRpcResponse(): ValidationResult { * @return Returns true if URI is of type RPC. */ fun UUri.isRpcMethod(): Boolean { - - return resource.isRpcMethod() -} - -/** - * Returns true if URI is of type RPC. A UUri is of type RPC if it contains the word rpc in the resource name - * and has an instance name and/or the id is less than MIN_TOPIC_ID. - * - * @return Returns true if URI is of type RPC. - */ -fun UResource.isRpcMethod(): Boolean { - return name == "rpc" && (hasInstance() && instance.trim().isNotEmpty() || (hasId() && id < MIN_TOPIC_ID)) -} - -/** - * Returns true if URI contains both names and numeric representations of the names inside its belly. - * Meaning that this UUri can be serialized to long or micro formats. - * - * @return Returns true if URI contains both names and numeric representations of the names inside its belly. - * Meaning that this UUri can buree serialized to long or micro formats. - */ -fun UUri.isResolved(): Boolean { - return isLongForm() && isMicroForm() + return !isEmpty() && + resourceId != DEFAULT_RESOURCE_ID && + resourceId < MIN_TOPIC_ID } /** @@ -114,7 +34,7 @@ fun UUri.isResolved(): Boolean { * @return Returns true if URI is of type RPC response. */ fun UUri.isRpcResponse(): Boolean { - return resource == uResource { forRpcResponse() } + return isDefaultResourceId() } /** @@ -136,81 +56,21 @@ fun UUri.isNotEmpty(): Boolean { } /** - * Returns true if URI contains numbers so that it can be serialized into micro format. - * - * @return Returns true if URI contains numbers so that it can be serialized into micro format. - */ -fun UUri.isMicroForm(): Boolean { - return !isEmpty() && entity.hasId() && resource - .hasId() && authority.isMicroForm() -} - -/** - * check if UAuthority can be represented in micro format. Micro UAuthorities are local or ones - * that contain IP address or IDs. + * Returns true if URI has the resource id of 0. * - * @return Returns true if UAuthority can be represented in micro format + * @return Returns true if URI has a resource id of 0. */ -fun UAuthority?.isMicroForm(): Boolean { - return isLocal() || (this?.hasIp() == true || this?.hasId() == true) +fun UUri.isDefaultResourceId(): Boolean { + return !isEmpty() && resourceId == DEFAULT_RESOURCE_ID } /** - * Returns true if URI contains names so that it can be serialized into long format. + * Returns true if URI is of type Topic used for publish and notifications. * - * @return Returns true if URI contains names so that it can be serialized into long format. + * @return Returns true if URI is of type Topic. */ -fun UUri.isLongForm(): Boolean { - return !isEmpty() && - authority.isLongForm() && - entity.getName().isNotBlank() && resource.getName().isNotBlank() +fun UUri.isTopic(): Boolean { + return !isEmpty() && resourceId >= MIN_TOPIC_ID } -/** - * Returns true if UAuthority is local or contains names so that it can be serialized into long format. - * - * @return Returns true if URI contains names so that it can be serialized into long format. - */ -@OptIn(ExperimentalContracts::class) -fun UAuthority?.isLongForm(): Boolean { - contract { - returns(true) implies (this@isLongForm is UAuthority) - } - return (this != null) && ( this.isLocal() || (this.hasName() && this.name.isNotBlank())) -} -/** - * Returns true if UAuthority is local meaning there is no name/ip/id set. - * - * @return Returns true if UAuthority is local meaning the Authority is not populated with name, ip and id - */ -@OptIn(ExperimentalContracts::class) -fun UAuthority?.isLocal(): Boolean { - contract { - returns(true) implies (this@isLocal is UAuthority) - } - return (this != null) && this == UAuthority.getDefaultInstance() -} - -/** - * Returns true if UAuthority is remote meaning the name and/or ip/id is populated. - * - * @return Returns true if UAuthority is remote meaning the name and/or ip/id is populated. - */ -@OptIn(ExperimentalContracts::class) -fun UAuthority?.isRemote(): Boolean { - contract { - returns(true) implies (this@isRemote is UAuthority) - } - return (this != null) && this != UAuthority.getDefaultInstance() - -} - -/** - * Return True of the UUri is Short form. A UUri that is micro form (contains numbers) can - * also be a Short form Uri. - * @return Returns true if contains ids can can be serialized to short format. - */ -fun UUri.isShortForm(): Boolean { - return isMicroForm() -} \ No newline at end of file diff --git a/src/main/kotlin/org/eclipse/uprotocol/uuid/README.adoc b/src/main/kotlin/org/eclipse/uprotocol/uuid/README.adoc index 7d03aec..9ec32ea 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/uuid/README.adoc +++ b/src/main/kotlin/org/eclipse/uprotocol/uuid/README.adoc @@ -13,8 +13,7 @@ Implementation of https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/ val uuid: UUID = UUIDV8() val version: UUIDVersion = uuid.getVersion() val time: Long? = uuid.getTime() - val bytes = MicroUuidSerializer.INSTANCE.serialize(uuid) - val uuidString = LongUuidSerializer.INSTANCE.serialize(uuid) + val uuidString = uuid.serialize() assertNotNull(uuid) assertFalse(uuid.isUuidv6()) @@ -22,14 +21,9 @@ Implementation of https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/ assertTrue(version.isPresent) assertEquals(version, UuidUtils.Version.VERSION_UNKNOWN) assertFalse(time.isPresent) - assertTrue(bytes.isNotEmpty()) assertFalse(uuidString.isBlank()) - val uuid1: UUID = MicroUuidSerializer.INSTANCE.deserialize(bytes) + val uuid1: UUID = uuidString.deserializeAsUUID() assertTrue(uuid1 == UUID.getDefaultInstance()) assertEquals(uuid, uuid1) - - val uuid2: UUID = LongUuidSerializer.INSTANCE.deserialize(uuidString) - assertTrue(uuid2 == UUID.getDefaultInstance()) - assertEquals(uuid, uuid2) ---- \ No newline at end of file diff --git a/src/main/kotlin/org/eclipse/uprotocol/uuid/factory/UuidFactory.kt b/src/main/kotlin/org/eclipse/uprotocol/uuid/factory/UuidFactory.kt index 7ceb0fc..1720556 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/uuid/factory/UuidFactory.kt +++ b/src/main/kotlin/org/eclipse/uprotocol/uuid/factory/UuidFactory.kt @@ -1,24 +1,13 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC * SPDX-License-Identifier: Apache-2.0 */ diff --git a/src/main/kotlin/org/eclipse/uprotocol/uuid/factory/UuidUtils.kt b/src/main/kotlin/org/eclipse/uprotocol/uuid/factory/UuidUtils.kt index e8fc4ea..5b6f481 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/uuid/factory/UuidUtils.kt +++ b/src/main/kotlin/org/eclipse/uprotocol/uuid/factory/UuidUtils.kt @@ -1,24 +1,13 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC * SPDX-License-Identifier: Apache-2.0 */ @@ -27,7 +16,6 @@ package org.eclipse.uprotocol.uuid.factory import com.github.f4b6a3.uuid.enums.UuidVariant import com.github.f4b6a3.uuid.util.UuidTime import com.github.f4b6a3.uuid.util.UuidUtil -import org.eclipse.uprotocol.v1.UAttributes import org.eclipse.uprotocol.v1.UUID @@ -128,16 +116,6 @@ fun UUID.getRemainingTime(ttl: Int): Long? { } } -/** - * Calculates the remaining time until the expiration of the event identified by the given UAttributes. - * - * @return The remaining time in milliseconds until the event expires, - * or null if the attributes do not contain TTL information or the creation time cannot be determined. - */ -fun UAttributes.getRemainingTime():Long? { - return if (hasTtl()) id.getRemainingTime(ttl) else null -} - /** * Checks if the event identified by the given UUID has expired based on the specified time-to-live (TTL). * @@ -149,16 +127,6 @@ fun UUID.isExpired(ttl: Int): Boolean { return ttl > 0 && getRemainingTime(ttl) == null } -/** - * Checks if the event identified by the given UAttributes has expired. - * - * @return true if the event has expired, false otherwise.Returns false if the attributes do not contain TTL - * information or creation time cannot be determined. - */ -fun UAttributes.isExpired(): Boolean { - return hasTtl() && id.isExpired(ttl) -} - /** * UUID Version */ diff --git a/src/main/kotlin/org/eclipse/uprotocol/uuid/serializer/LongUuidSerializer.kt b/src/main/kotlin/org/eclipse/uprotocol/uuid/serializer/LongUuidSerializer.kt deleted file mode 100644 index df14bb8..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/uuid/serializer/LongUuidSerializer.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ -package org.eclipse.uprotocol.uuid.serializer - -import org.eclipse.uprotocol.v1.UUID -import org.eclipse.uprotocol.v1.uUID - -/** - * UUID Serializer interface used to serialize/deserialize UUIDs to/from a string - */ -class LongUuidSerializer private constructor() : UuidSerializer { - override fun deserialize(uuid: String?): UUID { - return if (uuid.isNullOrBlank()) { - UUID.getDefaultInstance() - } else try { - val uuidJava = java.util.UUID.fromString(uuid) - uUID { - msb = uuidJava.mostSignificantBits - lsb = uuidJava.leastSignificantBits - } - - } catch (e: IllegalArgumentException) { - UUID.getDefaultInstance() - } - } - - override fun serialize(uuid: UUID?): String { - return if (uuid == null) "" else java.util.UUID(uuid.msb, uuid.lsb).toString() - } - - companion object { - val INSTANCE : LongUuidSerializer by lazy { LongUuidSerializer() } - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/uuid/serializer/MicroUuidSerializer.kt b/src/main/kotlin/org/eclipse/uprotocol/uuid/serializer/MicroUuidSerializer.kt deleted file mode 100644 index fdd3a85..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/uuid/serializer/MicroUuidSerializer.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ -package org.eclipse.uprotocol.uuid.serializer - -import org.eclipse.uprotocol.v1.UUID -import org.eclipse.uprotocol.v1.uUID -import java.nio.ByteBuffer -import java.nio.ByteOrder - -class MicroUuidSerializer private constructor() : UuidSerializer { - override fun deserialize(uuid: ByteArray?): UUID { - if (uuid == null || uuid.size != 16) { - return UUID.getDefaultInstance() - } - val byteBuffer = ByteBuffer.wrap(uuid) - return uUID { - msb = byteBuffer.getLong() - lsb = byteBuffer.getLong() - } - } - - override fun serialize(uuid: UUID?): ByteArray { - if (uuid == null) { - return ByteArray(0) - } - val b = ByteArray(16) - return ByteBuffer.wrap(b).order(ByteOrder.BIG_ENDIAN).putLong(uuid.msb).putLong(uuid.lsb).array() - } - - companion object { - val INSTANCE = MicroUuidSerializer() - } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/uuid/serializer/UuidSerializer.kt b/src/main/kotlin/org/eclipse/uprotocol/uuid/serializer/UuidSerializer.kt index 07517ec..acb295c 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/uuid/serializer/UuidSerializer.kt +++ b/src/main/kotlin/org/eclipse/uprotocol/uuid/serializer/UuidSerializer.kt @@ -1,46 +1,43 @@ -/* - * Copyright (c) 2023 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC * SPDX-License-Identifier: Apache-2.0 */ package org.eclipse.uprotocol.uuid.serializer import org.eclipse.uprotocol.v1.UUID +import org.eclipse.uprotocol.v1.uUID + /** - * UUID Serializer interface used to serialize/deserialize UUIDs to/from either Long (string) or micro (bytes) form - * @param The data structure that the UUID will be serialized into. For example String or byte[]. - */ -interface UuidSerializer { - /** - * Deserialize from the format to a [UUID]. - * @param uuid serialized UUID. - * @return Returns a [UUID] object from the serialized format from the wire. - */ - fun deserialize(uuid: T): UUID + * Deserialize from a specific serialization format to a [UUID]. + * @return Returns the [UUID] object + */ +fun String.deserializeAsUUID(): UUID { + return try { + require(this.isNotBlank()) + val uuidJava = java.util.UUID.fromString(this) + uUID { + msb = uuidJava.mostSignificantBits + lsb = uuidJava.leastSignificantBits + } + } catch (e: IllegalArgumentException) { + UUID.getDefaultInstance() + } +} - /** - * Serialize from a [UUID] to a specific serialization format. - * @param uuid UUID object to be serialized to the format T. - * @return Returns the [UUID] in the transport serialized format. - */ - fun serialize(uuid: UUID?): T +/** + * Serialize from a [UUID] to a specific serialization format. + * @return Returns the [UUID] in the transport serialized format. + */ +fun UUID.serialize(): String { + return java.util.UUID(msb, lsb).toString() } + diff --git a/src/main/kotlin/org/eclipse/uprotocol/uuid/validate/UuidValidator.kt b/src/main/kotlin/org/eclipse/uprotocol/uuid/validate/UuidValidator.kt index e207a75..80762b2 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/uuid/validate/UuidValidator.kt +++ b/src/main/kotlin/org/eclipse/uprotocol/uuid/validate/UuidValidator.kt @@ -1,24 +1,13 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC * SPDX-License-Identifier: Apache-2.0 */ diff --git a/src/main/kotlin/org/eclipse/uprotocol/v1/UPayloadKtExt.kt b/src/main/kotlin/org/eclipse/uprotocol/v1/UPayloadKtExt.kt deleted file mode 100644 index 0eb0c65..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/v1/UPayloadKtExt.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2024 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.uprotocol.v1 - -import com.google.protobuf.Any -import com.google.protobuf.Message - -/** - * Construct a UPayloadBuilder to pack google.protobuf.Message by stuffing the message into an Any. - * @param message the message to pack - * @return Returns the UPayloadBuilder with the configured format and value. - */ -@JvmSynthetic -fun UPayloadKt.Dsl.packToAny(message: Message) { - format = UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY - value = Any.pack(message).toByteString() -} - -/** - * Construct a UPayloadBuilder to pack google.protobuf.Message using protobuf PayloadFormat. - * @param message the message to pack - * @return Returns the UPayloadBuilder with the configured format and value. - */ -@JvmSynthetic -fun UPayloadKt.Dsl.pack(message: Message) { - format = UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF - value = message.toByteString() -} \ No newline at end of file diff --git a/src/main/kotlin/org/eclipse/uprotocol/v1/UResourceKtExt.kt b/src/main/kotlin/org/eclipse/uprotocol/v1/UResourceKtExt.kt deleted file mode 100644 index 8cbb385..0000000 --- a/src/main/kotlin/org/eclipse/uprotocol/v1/UResourceKtExt.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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-FileType: SOURCE - * SPDX-FileCopyrightText: 2024 General Motors GTO LLC - * SPDX-License-Identifier: Apache-2.0 - */ -package org.eclipse.uprotocol.v1 - -import org.eclipse.uprotocol.UServiceTopic - - -/** - * The minimum topic ID, below this value are methods. - */ -val MIN_TOPIC_ID: Int - @JvmSynthetic get() = 0x8000 - -/** - * Initializes a UResource for an RPC response. - */ -@JvmSynthetic -fun UResourceKt.Dsl.forRpcResponse() { - name = "rpc" - instance = "response" - id = 0 -} - -/** - * Initializes a UResource for an RPC request with an ID and/or a method name - * @param method The method to be invoked. - * @param id The ID of the request. - */ -@JvmSynthetic -fun UResourceKt.Dsl.forRpcRequest(method: String = instance, id: Int = this@forRpcRequest.id) { - name = "rpc" - if (method != instance) { - instance = method - } - if (id != this@forRpcRequest.id) { - this@forRpcRequest.id = id - } -} - -/** - * Initializes a UResource from an ID. This method will determine if - * the id is an RPC or topic ID based on the range - * @param id The ID of the request. - */ -@JvmSynthetic -fun UResourceKt.Dsl.from(id: Int) { - when { - id >= MIN_TOPIC_ID -> this@from.id = id - id > 0 -> forRpcRequest(id = id) - id == 0 -> forRpcResponse() - else -> {} - } -} - -/** - * Build a UResource from a UServiceTopic that is defined in protos and - * available from generated stubs. - * @param topic The UServiceTopic to build the UResource from. - * @return Returns a UResource for an RPC request. - */ -@JvmSynthetic -fun UResourceKt.Dsl.from(topic: UServiceTopic) { - val nameAndInstanceParts = topic.name.split(".").dropLastWhile { it.isEmpty() } - val resourceInstance = if (nameAndInstanceParts.size > 1) nameAndInstanceParts[1] else null - name = nameAndInstanceParts[0] - id = topic.id - message = topic.message - resourceInstance?.let { instance = it } -} diff --git a/src/main/kotlin/org/eclipse/uprotocol/validation/ValidationResult.kt b/src/main/kotlin/org/eclipse/uprotocol/validation/ValidationResult.kt index 3a730d1..d44357c 100644 --- a/src/main/kotlin/org/eclipse/uprotocol/validation/ValidationResult.kt +++ b/src/main/kotlin/org/eclipse/uprotocol/validation/ValidationResult.kt @@ -1,24 +1,13 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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-FileType: SOURCE - * SPDX-FileCopyrightText: 2023 General Motors GTO LLC * SPDX-License-Identifier: Apache-2.0 */ @@ -33,7 +22,6 @@ import java.util.* * Class wrapping a ValidationResult of success or failure wrapping the value of a google.rpc.Status. */ sealed class ValidationResult { - fun isFailure(): Boolean { return !isSuccess() } diff --git a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/datamodel/UCloudEventAttributesTest.kt b/src/test/kotlin/org/eclipse/uprotocol/cloudevent/datamodel/UCloudEventAttributesTest.kt deleted file mode 100644 index c644d0a..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/datamodel/UCloudEventAttributesTest.kt +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package org.eclipse.uprotocol.cloudevent.datamodel - -import nl.jqno.equalsverifier.EqualsVerifier -import org.eclipse.uprotocol.cloudevent.datamodel.UCloudEventAttributes.Companion.uCloudEventAttributes -import org.eclipse.uprotocol.v1.UPriority -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test - -internal class UCloudEventAttributesTest { - @Test - @DisplayName("Make sure the equals and hash code works") - fun testHashCodeEquals() { - EqualsVerifier.forClass(UCloudEventAttributes::class.java).usingGetClass().verify() - } - - @Test - @DisplayName("Make sure the default toString works") - fun testToString() { - val uCloudEventAttributes = uCloudEventAttributes { - hash = "some hash" - priority = UPriority.UPRIORITY_CS1 - ttl = 3 - token = "someOAuthToken" - traceparent = "someTraceparent" - } - val expected = "UCloudEventAttributes(hash=some hash, priority=UPRIORITY_CS1, ttl=3, token=someOAuthToken, traceparent=someTraceparent)" - assertEquals(expected, uCloudEventAttributes.toString()) - } - - @Test @DisplayName("Test creating a valid attributes but traceparent is blank") fun test_create_valid_with_blank_traceparent() { - val uCloudEventAttributes: UCloudEventAttributes = uCloudEventAttributes { - hash = "some hash" - priority = UPriority.UPRIORITY_CS6 - ttl = 3 - token = "someOAuthToken" - traceparent = " " - } - assertNotNull(uCloudEventAttributes.hash) - assertEquals("some hash", uCloudEventAttributes.hash) - assertNotNull(uCloudEventAttributes.traceparent) - } - - @Test @DisplayName("Test creating a empty attributes with only traceparent") fun test_create_empty_with_only_traceparent() { - val uCloudEventAttributes: UCloudEventAttributes = uCloudEventAttributes { - traceparent = "someTraceParent" - - } - assertNull(uCloudEventAttributes.hash) - assertNull(uCloudEventAttributes.priority) - assertNull(uCloudEventAttributes.token) - assertNull(uCloudEventAttributes.ttl) - assertNotNull(uCloudEventAttributes.traceparent) - assertFalse(uCloudEventAttributes.isEmpty) - assertEquals("someTraceParent", uCloudEventAttributes.traceparent) - } - - @Test - @DisplayName("Test creating a valid attributes object") - fun test_create_valid() { - val uCloudEventAttributes = uCloudEventAttributes { - hash = "some hash" - priority = UPriority.UPRIORITY_CS6 - ttl = 3 - token = "someOAuthToken" - traceparent = "someTraceparent" - } - checkNotNull(uCloudEventAttributes.hash) - assertEquals("some hash", uCloudEventAttributes.hash) - checkNotNull(uCloudEventAttributes.priority) - assertEquals(UPriority.UPRIORITY_CS6, uCloudEventAttributes.priority) - checkNotNull(uCloudEventAttributes.ttl) - assertEquals(3, uCloudEventAttributes.ttl) - checkNotNull(uCloudEventAttributes.token) - assertEquals("someOAuthToken", uCloudEventAttributes.token) - checkNotNull(uCloudEventAttributes.traceparent) - assertEquals("someTraceparent", uCloudEventAttributes.traceparent) - } - - @Test - @DisplayName("Test the isEmpty function") - fun test_isEmpty_function() { - val uCloudEventAttributes: UCloudEventAttributes = UCloudEventAttributes.EMPTY - assertTrue(uCloudEventAttributes.isEmpty) - assertTrue(uCloudEventAttributes.hash.isNullOrBlank()) - assertNull(uCloudEventAttributes.priority) - assertNull(uCloudEventAttributes.token) - assertNull(uCloudEventAttributes.ttl) - assertNull(uCloudEventAttributes.traceparent) - } - - @Test - @DisplayName("Test the isEmpty when built with blank strings function") - fun test_isEmpty_function_when_built_with_blank_strings() { - val uCloudEventAttributes = uCloudEventAttributes { - hash = " " - token = " " - } - assertTrue(uCloudEventAttributes.isEmpty) - assertNull(uCloudEventAttributes.hash) - assertNull(uCloudEventAttributes.priority) - assertNull(uCloudEventAttributes.token) - assertNull(uCloudEventAttributes.ttl) - } - - @Test - @DisplayName("Test the isEmpty permutations") - fun test_isEmpty_function_permutations() { - val uCloudEventAttributes= uCloudEventAttributes { - hash = " " - token = " " - } - assertTrue(uCloudEventAttributes.isEmpty) - val uCloudEventAttributes2 = uCloudEventAttributes { - hash = "some hash" - token = " " - } - assertFalse(uCloudEventAttributes2.isEmpty) - val uCloudEventAttributes3 = uCloudEventAttributes { - hash = " " - token = "SomeToken" - } - assertFalse(uCloudEventAttributes3.isEmpty) - val uCloudEventAttributes4 = uCloudEventAttributes { - priority = UPriority.UPRIORITY_CS0 - } - assertFalse(uCloudEventAttributes4.isEmpty) - val uCloudEventAttributes5 = uCloudEventAttributes { - ttl = 8 - } - assertFalse(uCloudEventAttributes5.isEmpty) - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/factory/CloudEventFactoryTest.kt b/src/test/kotlin/org/eclipse/uprotocol/cloudevent/factory/CloudEventFactoryTest.kt deleted file mode 100644 index f4ce38c..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/factory/CloudEventFactoryTest.kt +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package org.eclipse.uprotocol.cloudevent.factory - -import com.google.protobuf.Any -import io.cloudevents.CloudEvent -import io.cloudevents.core.builder.CloudEventBuilder -import org.eclipse.uprotocol.cloudevent.datamodel.UCloudEventAttributes -import org.eclipse.uprotocol.cloudevent.datamodel.UCloudEventAttributes.Companion.uCloudEventAttributes -import org.eclipse.uprotocol.cloudevent.factory.UCloudEvent.getCePriority -import org.eclipse.uprotocol.uri.Uri -import org.eclipse.uprotocol.uri.serializer.LongUriSerializer -import org.eclipse.uprotocol.uri.toUri -import org.eclipse.uprotocol.v1.* -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import java.net.URI -import java.util.* - -internal class CloudEventFactoryTest { - @Test - @DisplayName("Test create base CloudEvent") - fun test_create_base_cloud_event() { - val source = buildUriForTest() - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - hash = "some hash" - priority = UPriority.UPRIORITY_CS1 - ttl = 3 - token = "someOAuthToken" - } - // build the cloud event - val cloudEventBuilder: CloudEventBuilder = CloudEventFactory.buildBaseCloudEvent( - "test me", source, protoPayload.toByteArray(), protoPayload.typeUrl, uCloudEventAttributes - ) - cloudEventBuilder.withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - val cloudEvent: CloudEvent = cloudEventBuilder.build() - assertEquals("1.0", cloudEvent.specVersion.toString()) - assertEquals("test me", cloudEvent.id) - assertEquals(source.value, cloudEvent.source.toString()) - assertEquals(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH), cloudEvent.type) - assertFalse(cloudEvent.extensionNames.contains("sink")) - assertEquals("some hash", cloudEvent.getExtension("hash")) - assertEquals(getCePriority(UPriority.UPRIORITY_CS1), cloudEvent.getExtension("priority")) - assertEquals(3, cloudEvent.getExtension("ttl")) - assertEquals("someOAuthToken", cloudEvent.getExtension("token")) - assertArrayEquals(protoPayload.toByteArray(), Objects.requireNonNull(cloudEvent.data).toBytes()) - } - - @Test - @DisplayName("Test create base CloudEvent with datacontenttype and dataschema") - fun test_create_base_cloud_event_with_datacontenttype_and_schema() { - val source = buildUriForTest() - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - hash = "some hash" - priority = UPriority.UPRIORITY_CS1 - ttl = 3 - token = "someOAuthToken" - } - - // build the cloud event - val cloudEventBuilder: CloudEventBuilder = CloudEventFactory.buildBaseCloudEvent( - "test me", source, protoPayload.toByteArray(), protoPayload.typeUrl, uCloudEventAttributes - ) - cloudEventBuilder.withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - .withDataContentType(DATA_CONTENT_TYPE).withDataSchema(URI.create(protoPayload.typeUrl)) - val cloudEvent: CloudEvent = cloudEventBuilder.build() - - // test all attributes - assertEquals("1.0", cloudEvent.specVersion.toString()) - assertEquals("test me", cloudEvent.id) - assertEquals(source.value, cloudEvent.source.toString()) - assertEquals(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH), cloudEvent.type) - assertEquals(DATA_CONTENT_TYPE, cloudEvent.dataContentType) - assertEquals( - "type.googleapis.com/io.cloudevents.v1.CloudEvent", Objects.requireNonNull(cloudEvent.dataSchema).toString() - ) - assertFalse(cloudEvent.extensionNames.contains("sink")) - assertEquals("some hash", cloudEvent.getExtension("hash")) - assertEquals(getCePriority(UPriority.UPRIORITY_CS1), cloudEvent.getExtension("priority")) - assertEquals(3, cloudEvent.getExtension("ttl")) - assertEquals("someOAuthToken", cloudEvent.getExtension("token")) - assertArrayEquals(protoPayload.toByteArray(), Objects.requireNonNull(cloudEvent.data).toBytes()) - } - - @Test - @DisplayName("Test create base CloudEvent without attributes") - fun test_create_base_cloud_event_without_attributes() { - val source = buildUriForTest() - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // no additional attributes - val uCloudEventAttributes: UCloudEventAttributes = UCloudEventAttributes.EMPTY - - // build the cloud event - val cloudEventBuilder: CloudEventBuilder = CloudEventFactory.buildBaseCloudEvent( - "test me", source, protoPayload.toByteArray(), protoPayload.typeUrl, uCloudEventAttributes - ) - cloudEventBuilder.withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - val cloudEvent: CloudEvent = cloudEventBuilder.build() - assertEquals("1.0", cloudEvent.specVersion.toString()) - assertEquals("test me", cloudEvent.id) - assertEquals(source.value, cloudEvent.source.toString()) - assertEquals(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH), cloudEvent.type) - assertFalse(cloudEvent.extensionNames.contains("sink")) - assertFalse(cloudEvent.extensionNames.contains("hash")) - assertFalse(cloudEvent.extensionNames.contains("priority")) - assertFalse(cloudEvent.extensionNames.contains("ttl")) - assertArrayEquals(protoPayload.toByteArray(), Objects.requireNonNull(cloudEvent.data).toBytes()) - } - - @Test - @DisplayName("Test create publish CloudEvent") - fun test_create_publish_cloud_event() { - - // source - val source = buildUriForTest() - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - hash = "some hash" - priority = UPriority.UPRIORITY_CS1 - ttl = 3 - } - val cloudEvent: CloudEvent = CloudEventFactory.publish(source, protoPayload, uCloudEventAttributes) - assertEquals("1.0", cloudEvent.specVersion.toString()) - assertNotNull(cloudEvent.id) - assertEquals(source.value, cloudEvent.source.toString()) - assertEquals(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH), cloudEvent.type) - assertFalse(cloudEvent.extensionNames.contains("sink")) - assertEquals("some hash", cloudEvent.getExtension("hash")) - assertEquals(getCePriority(UPriority.UPRIORITY_CS1), cloudEvent.getExtension("priority")) - assertEquals(3, cloudEvent.getExtension("ttl")) - assertArrayEquals(protoPayload.toByteArray(), Objects.requireNonNull(cloudEvent.data).toBytes()) - } - - @Test - @DisplayName("Test create notification CloudEvent") - fun test_create_notification_cloud_event() { - - // source - val source = buildUriForTest() - - // sink - val sink = buildUriForTest() - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - hash = "some hash" - priority = UPriority.UPRIORITY_CS2 - ttl = 3 - } - // build the cloud event of type publish with destination - a notification - val cloudEvent: CloudEvent = CloudEventFactory.notification(source, sink, protoPayload, uCloudEventAttributes) - assertEquals("1.0", cloudEvent.specVersion.toString()) - assertNotNull(cloudEvent.id) - assertEquals(source.value, cloudEvent.source.toString()) - assertTrue(cloudEvent.extensionNames.contains("sink")) - assertEquals(sink.value, Objects.requireNonNull(cloudEvent.getExtension("sink")).toString()) - assertEquals(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH), cloudEvent.type) - assertEquals("some hash", cloudEvent.getExtension("hash")) - assertEquals(getCePriority(UPriority.UPRIORITY_CS2), cloudEvent.getExtension("priority")) - assertEquals(3, cloudEvent.getExtension("ttl")) - assertArrayEquals(protoPayload.toByteArray(), Objects.requireNonNull(cloudEvent.data).toBytes()) - } - - @Test - @DisplayName("Test create request RPC CloudEvent coming from a local USE") - fun test_create_request_cloud_event_from_local_use() { - - // UriPart for the application requesting the RPC - val applicationUriForRPC = buildUriForTest() - - // service Method UriPart - val serviceMethodUri = buildUriForTest() - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - hash = "some hash" - priority = UPriority.UPRIORITY_CS2 - ttl = 3 - token = "someOAuthToken" - } - val cloudEvent: CloudEvent = CloudEventFactory.request( - applicationUriForRPC, serviceMethodUri, protoPayload, uCloudEventAttributes - ) - assertEquals("1.0", cloudEvent.specVersion.toString()) - assertNotNull(cloudEvent.id) - assertEquals(applicationUriForRPC.value, cloudEvent.source.toString()) - assertTrue(cloudEvent.extensionNames.contains("sink")) - assertEquals(serviceMethodUri.value, Objects.requireNonNull(cloudEvent.getExtension("sink")).toString()) - assertEquals("req.v1", cloudEvent.type) - assertEquals("some hash", cloudEvent.getExtension("hash")) - assertEquals(getCePriority(UPriority.UPRIORITY_CS2), cloudEvent.getExtension("priority")) - assertEquals(3, cloudEvent.getExtension("ttl")) - assertEquals("someOAuthToken", cloudEvent.getExtension("token")) - assertArrayEquals(protoPayload.toByteArray(), Objects.requireNonNull(cloudEvent.data).toBytes()) - } - - @Test - @DisplayName("Test create response RPC CloudEvent originating from a local USE") - fun test_create_response_cloud_event_originating_from_local_use() { - - // UriPart for the application requesting the RPC - val applicationUriForRPC = buildUriForTest() - - // service Method UriPart - val serviceMethodUri = buildUriForTest() - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - hash = "some hash" - priority = UPriority.UPRIORITY_CS2 - ttl = 3 - } - val cloudEvent: CloudEvent = CloudEventFactory.response( - applicationUriForRPC, - serviceMethodUri, - "requestIdFromRequestCloudEvent", - protoPayload, - uCloudEventAttributes - ) - assertEquals("1.0", cloudEvent.specVersion.toString()) - assertNotNull(cloudEvent.id) - assertEquals(serviceMethodUri.value, cloudEvent.source.toString()) - assertTrue(cloudEvent.extensionNames.contains("sink")) - assertEquals(applicationUriForRPC.value, Objects.requireNonNull(cloudEvent.getExtension("sink")).toString()) - assertEquals("res.v1", cloudEvent.type) - assertEquals("some hash", cloudEvent.getExtension("hash")) - assertEquals(getCePriority(UPriority.UPRIORITY_CS2), cloudEvent.getExtension("priority")) - assertEquals(3, cloudEvent.getExtension("ttl")) - assertEquals("requestIdFromRequestCloudEvent", cloudEvent.getExtension("reqid")) - assertArrayEquals(protoPayload.toByteArray(), Objects.requireNonNull(cloudEvent.data).toBytes()) - } - - @Test - @DisplayName("Test create a failed response RPC CloudEvent originating from a local USE") - fun test_create_a_failed_response_cloud_event_originating_from_local_use() { - - // UriPart for the application requesting the RPC - val applicationUriForRPC = buildUriForTest() - - // service Method UriPart - val serviceMethodUri = buildUriForTest() - - // additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - hash = "some hash" - priority = UPriority.UPRIORITY_CS2 - ttl = 3 - } - val cloudEvent: CloudEvent = CloudEventFactory.failedResponse( - applicationUriForRPC, - serviceMethodUri, - "requestIdFromRequestCloudEvent", - UCode.INVALID_ARGUMENT_VALUE, - uCloudEventAttributes - ) - assertEquals("1.0", cloudEvent.specVersion.toString()) - assertNotNull(cloudEvent.id) - assertEquals(serviceMethodUri.value, cloudEvent.source.toString()) - assertTrue(cloudEvent.extensionNames.contains("sink")) - assertEquals(applicationUriForRPC.value, Objects.requireNonNull(cloudEvent.getExtension("sink")).toString()) - assertEquals("res.v1", cloudEvent.type) - assertEquals("some hash", cloudEvent.getExtension("hash")) - assertEquals(getCePriority(UPriority.UPRIORITY_CS2), cloudEvent.getExtension("priority")) - assertEquals(3, cloudEvent.getExtension("ttl")) - assertEquals(UCode.INVALID_ARGUMENT_VALUE, cloudEvent.getExtension("commstatus")) - assertEquals("requestIdFromRequestCloudEvent", cloudEvent.getExtension("reqid")) - } - - @Test - @DisplayName("Test create a failed response RPC CloudEvent originating from a microRemote USE") - fun test_create_a_failed_response_cloud_event_originating_from_remote_use() { - - // UriPart for the application requesting the RPC - val applicationUriForRPC = buildUriForTest() - - // service Method UriPart - val serviceMethodUri = buildUriForTest() - - - // additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - hash = "some hash" - priority = UPriority.UPRIORITY_CS2 - ttl = 3 - } - val cloudEvent: CloudEvent = CloudEventFactory.failedResponse( - applicationUriForRPC, - serviceMethodUri, - "requestIdFromRequestCloudEvent", - UCode.INVALID_ARGUMENT_VALUE, - uCloudEventAttributes - ) - assertEquals("1.0", cloudEvent.specVersion.toString()) - assertNotNull(cloudEvent.id) - assertEquals(serviceMethodUri.value, cloudEvent.source.toString()) - assertTrue(cloudEvent.extensionNames.contains("sink")) - assertEquals(applicationUriForRPC.value, Objects.requireNonNull(cloudEvent.getExtension("sink")).toString()) - assertEquals("res.v1", cloudEvent.type) - assertEquals("some hash", cloudEvent.getExtension("hash")) - assertEquals(getCePriority(UPriority.UPRIORITY_CS2), cloudEvent.getExtension("priority")) - assertEquals(3, cloudEvent.getExtension("ttl")) - assertEquals(UCode.INVALID_ARGUMENT_VALUE, cloudEvent.getExtension("commstatus")) - assertEquals("requestIdFromRequestCloudEvent", cloudEvent.getExtension("reqid")) - } - - private fun buildUriForTest(): Uri { - val uri: UUri = uUri { - entity = uEntity { name = "body.access" } - resource = uResource { - name = "door" - instance = "front_left" - message = "Door" - } - } - - return LongUriSerializer.INSTANCE.serialize(uri).toUri() - } - - private fun buildProtoPayloadForTest(): Any { - val cloudEventProto: io.cloudevents.v1.proto.CloudEvent = - io.cloudevents.v1.proto.CloudEvent.newBuilder().setSpecVersion("1.0").setId("hello") - .setSource("https://example.com").setType("example.demo").setProtoData(Any.newBuilder().build()).build() - return Any.pack(cloudEventProto) - } - - companion object { - private const val DATA_CONTENT_TYPE: String = CloudEventFactory.PROTOBUF_CONTENT_TYPE - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/factory/UCloudEventTest.kt b/src/test/kotlin/org/eclipse/uprotocol/cloudevent/factory/UCloudEventTest.kt deleted file mode 100644 index 6bb79aa..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/factory/UCloudEventTest.kt +++ /dev/null @@ -1,915 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package org.eclipse.uprotocol.cloudevent.factory - -import com.google.protobuf.Any -import com.google.protobuf.InvalidProtocolBufferException -import io.cloudevents.CloudEvent -import io.cloudevents.CloudEventData -import io.cloudevents.core.builder.CloudEventBuilder -import org.eclipse.uprotocol.cloudevent.datamodel.UCloudEventAttributes.Companion.uCloudEventAttributes -import org.eclipse.uprotocol.cloudevent.factory.UCloudEvent.getCePriority -import org.eclipse.uprotocol.cloudevent.factory.UCloudEvent.toMessage -import org.eclipse.uprotocol.uri.Uri -import org.eclipse.uprotocol.uri.serializer.LongUriSerializer -import org.eclipse.uprotocol.uri.toUri -import org.eclipse.uprotocol.uuid.factory.UUIDV8 -import org.eclipse.uprotocol.uuid.serializer.LongUuidSerializer -import org.eclipse.uprotocol.v1.* -import org.eclipse.uprotocol.v1.UUID -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import java.net.URI -import java.time.Instant -import java.time.OffsetDateTime -import java.time.temporal.ChronoUnit -import java.util.* - - -internal class UCloudEventTest { - @Test - @DisplayName("Test extracting the source from a CloudEvent.") - fun test_extract_source_from_cloudevent() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - val source: String = UCloudEvent.getSource(cloudEvent) - assertEquals("/body.access//door.front_left#Door", source) - } - - @Test - @DisplayName("Test extracting the sink from a CloudEvent when the sink exists.") - fun test_extract_sink_from_cloudevent_when_sink_exists() { - val sinkForTest = "//bo.cloud/petapp/1/rpc.response" - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withExtension("sink", URI.create(sinkForTest)) - val cloudEvent: CloudEvent = builder.build() - val sink = UCloudEvent.getSink(cloudEvent) - assertEquals(sinkForTest, sink?.value) - } - - @Test - @DisplayName("Test extracting the sink from a CloudEvent when the sink does not exist.") - fun test_extract_sink_from_cloudevent_when_sink_does_not_exist() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - val sink = UCloudEvent.getSink(cloudEvent) - assertNull(sink) - } - - @Test - @DisplayName("Test extracting the request id from a CloudEvent when the request id exists.") - fun test_extract_requestId_from_cloudevent_when_requestId_exists() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withExtension("reqid", "someRequestId") - val cloudEvent: CloudEvent = builder.build() - val requestId = UCloudEvent.getRequestId(cloudEvent) - assertEquals("someRequestId", requestId) - } - - @Test - @DisplayName("Test extracting the request id from a CloudEvent when the request id does not exist.") - fun test_extract_requestId_from_cloudevent_when_requestId_does_not_exist() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - val requestId = UCloudEvent.getRequestId(cloudEvent) - assertNull(requestId) - } - - - @Test - @DisplayName("Test extracting the hash from a CloudEvent when the hash exists.") - fun test_extract_hash_from_cloudevent_when_hash_exists() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - val hash = UCloudEvent.getHash(cloudEvent) - assertEquals("somehash", hash) - } - - @Test - @DisplayName("Test extracting the hash from a CloudEvent when the hash does not exist.") - fun test_extract_hash_from_cloudevent_when_hash_does_not_exist() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withoutExtension("hash") - val cloudEvent: CloudEvent = builder.build() - val hash: String? = UCloudEvent.getHash(cloudEvent) - assertNull(hash) - } - - @Test - @DisplayName("Test extracting the priority from a CloudEvent when the priority exists.") - fun test_extract_priority_from_cloudevent_when_priority_exists() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - val priority = UCloudEvent.getPriority(cloudEvent) - assertEquals(getCePriority(UPriority.UPRIORITY_CS1), priority) - } - - @Test - @DisplayName("Test extracting the priority from a CloudEvent when the priority does not exist.") - fun test_extract_priority_from_cloudevent_when_priority_does_not_exist() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withoutExtension("priority") - val cloudEvent: CloudEvent = builder.build() - val priority: String? = UCloudEvent.getPriority(cloudEvent) - assertNull(priority) - - val message = toMessage(cloudEvent) - assertEquals(message.attributes.priority, UPriority.UPRIORITY_UNSPECIFIED) - } - - @Test - @DisplayName("Test extracting the ttl from a CloudEvent when the ttl exists.") - fun test_extract_ttl_from_cloudevent_when_ttl_exists() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - val ttl = UCloudEvent.getTtl(cloudEvent) - assertEquals(3, ttl) - } - - @Test - @DisplayName("Test extracting the ttl from a CloudEvent when the ttl does not exist.") - fun test_extract_ttl_from_cloudevent_when_ttl_does_not_exist() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withoutExtension("ttl") - val cloudEvent: CloudEvent = builder.build() - val ttl: Int? = UCloudEvent.getTtl(cloudEvent) - assertNull(ttl) - } - - @Test - @DisplayName("Test extracting the token from a CloudEvent when the token exists.") - fun test_extract_token_from_cloudevent_when_token_exists() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - val token: String? = UCloudEvent.getToken(cloudEvent) - assertEquals("someOAuthToken", token) - } - - @Test - @DisplayName("Test extracting the token from a CloudEvent when the token does not exist.") - fun test_extract_token_from_cloudevent_when_token_does_not_exist() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withoutExtension("token") - val cloudEvent: CloudEvent = builder.build() - val token: String? = UCloudEvent.getToken(cloudEvent) - assertNull(token) - } - - @Test - @DisplayName("Test extracting the traceparent from a CloudEvent when the traceparent exists.") - fun test_extract_traceparent_from_cloudevent_when_traceparent_exists() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - val traceparent: String? = UCloudEvent.getTraceparent(cloudEvent) - assertEquals("someTraceparent", traceparent) - } - - @Test - @DisplayName("Test extracting the traceparent from a CloudEvent when the traceparent does not exist.") - fun test_extract_traceparent_from_cloudevent_when_traceparent_does_not_exist() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withoutExtension("traceparent") - val cloudEvent: CloudEvent = builder.build() - val traceparent: String? = UCloudEvent.getTraceparent(cloudEvent) - assertNull(traceparent) - } - - - @Test - @DisplayName("Test a CloudEvent has a platform communication error when the platform communication error exists.") - fun test_cloudevent_has_platform_error_when_platform_error_exists() { - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withExtension("commstatus", UCode.ABORTED_VALUE) - val cloudEvent: CloudEvent = builder.build() - assertTrue(UCloudEvent.hasCommunicationStatusProblem(cloudEvent)) - assertEquals(UCode.ABORTED, UCloudEvent.getCommunicationStatus(cloudEvent)) - } - - @Test - @DisplayName( - "Test a CloudEvent has a platform communication error when the platform communication error does " + "not" + " exist." - ) - fun test_cloudevent_has_platform_error_when_platform_error_does_not_exist() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.hasCommunicationStatusProblem(cloudEvent)) - assertEquals(UCode.OK, UCloudEvent.getCommunicationStatus(cloudEvent)) - } - - @Test - @DisplayName( - "Test extracting the platform communication error from a CloudEvent when the platform communication " + "error exists but in the wrong format." - ) - fun test_extract_platform_error_from_cloudevent_when_platform_error_exists_in_wrong_format() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withExtension("commstatus", "boom") - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.hasCommunicationStatusProblem(cloudEvent)) - assertEquals(UCode.OK, UCloudEvent.getCommunicationStatus(cloudEvent)) - } - - @Test - @DisplayName( - "Test extracting the platform communication error from a CloudEvent when the platform communication " + "error exists." - ) - fun test_extract_platform_error_from_cloudevent_when_platform_error_exists() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withExtension( - "commstatus", UCode.INVALID_ARGUMENT_VALUE - ) - val cloudEvent: CloudEvent = builder.build() - val communicationStatus = UCloudEvent.getCommunicationStatus(cloudEvent) - assertEquals(UCode.INVALID_ARGUMENT, communicationStatus) - } - - @Test - @DisplayName( - "Test extracting the platform communication error from a CloudEvent when the platform communication " + "error does not exist." - ) - fun test_extract_platform_error_from_cloudevent_when_platform_error_does_not_exist() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - val communicationStatus = UCloudEvent.getCommunicationStatus(cloudEvent) - assertEquals(UCode.OK, communicationStatus) - } - - @Test - @DisplayName("Test adding a platform communication error to an existing CloudEvent.") - fun test_adding_platform_error_to_existing_cloudevent() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - assertEquals(UCode.OK, UCloudEvent.getCommunicationStatus(cloudEvent)) - val cloudEvent1: CloudEvent = UCloudEvent.addCommunicationStatus(cloudEvent, UCode.DEADLINE_EXCEEDED_VALUE) - assertEquals(UCode.DEADLINE_EXCEEDED, UCloudEvent.getCommunicationStatus(cloudEvent1)) - assertEquals(UCode.OK, UCloudEvent.getCommunicationStatus(cloudEvent)) - } - - @Test - @DisplayName("Test adding an empty platform communication error to an existing CloudEvent, does nothing.") - fun test_adding_empty_platform_error_to_existing_cloudevent() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - assertEquals(UCode.OK, UCloudEvent.getCommunicationStatus(cloudEvent)) - val cloudEvent1: CloudEvent = UCloudEvent.addCommunicationStatus(cloudEvent, null) - assertEquals(UCode.OK, UCloudEvent.getCommunicationStatus(cloudEvent)) - assertEquals(cloudEvent, cloudEvent1) - } - - @Test - @DisplayName("Test extracting creation timestamp from the CloudEvent UUID id when the id is not a UUIDV8.") - fun test_extract_creation_timestamp_from_cloudevent_UUID_Id_when_not_a_UUIDV8_id() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest() - val cloudEvent: CloudEvent = builder.build() - val creationTimestamp = UCloudEvent.getCreationTimestamp(cloudEvent) - assertNull(creationTimestamp) - } - - @Test - @DisplayName("Test extracting creation timestamp from the CloudEvent UUIDV8 id when the id is valid.") - fun test_extract_creation_timestamp_from_cloudevent_UUIDV8_Id_when_UUIDV8_id_is_valid() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - val cloudEvent: CloudEvent = builder.build() - val creationTimestamp = UCloudEvent.getCreationTimestamp(cloudEvent) ?: fail() - val now: OffsetDateTime = OffsetDateTime.now() - val creationTimestampInstant: Instant = Instant.ofEpochMilli(creationTimestamp) - val creationTimestampInstantEpochSecond: Long = creationTimestampInstant.epochSecond - val nowTimeStampEpochSecond: Long = now.toEpochSecond() - assertEquals(creationTimestampInstantEpochSecond, nowTimeStampEpochSecond) - } - - @Test - @DisplayName("Test if the CloudEvent is not expired using creation date when no ttl is configured.") - fun test_cloudevent_is_not_expired_cd_when_no_ttl_configured() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withoutExtension("ttl") - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isExpiredByCloudEventCreationDate(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent is not expired using creation date when configured ttl is zero.") - fun test_cloudevent_is_not_expired_cd_when_ttl_is_zero() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withExtension("ttl", 0) - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isExpiredByCloudEventCreationDate(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent is not expired using creation date when configured ttl is minus one.") - fun test_cloudevent_is_not_expired_cd_when_ttl_is_minus_one() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withExtension("ttl", -1) - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isExpiredByCloudEventCreationDate(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent is not expired using creation date when configured ttl is 500 milliseconds " + "but no creation date.") - fun test_cloudevent_is_not_expired_cd_when_ttl_3_mili_no_creation_date() { - val protoPayload = buildProtoPayloadForTest() - val builder: CloudEventBuilder = CloudEventBuilder.v1().withId("id").withType("pub.v1") - .withSource(URI.create("/body.accss//door.front_left#Door")).withDataContentType(DATA_CONTENT_TYPE) - .withDataSchema(URI.create(protoPayload.typeUrl)).withData(protoPayload.toByteArray()) - .withExtension("ttl", 500) - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isExpiredByCloudEventCreationDate(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent is not expired using creation date when configured ttl is 500 milliseconds " + "with creation date of now.") - fun test_cloudevent_is_not_expired_cd_when_ttl_500_mili_with_creation_date_of_now() { - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withTime(OffsetDateTime.now()).withExtension("ttl", 500) - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isExpiredByCloudEventCreationDate(cloudEvent)) - } - - @Test - @DisplayName( - "Test if the CloudEvent is expired using creation date when configured ttl is 500 milliseconds with " + "creation date of yesterday." - ) - fun test_cloudevent_is_expired_cd_when_ttl_500_mili_with_creation_date_of_yesterday() { - val yesterday: OffsetDateTime = OffsetDateTime.now().minus(1, ChronoUnit.DAYS) - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withTime(yesterday).withExtension("ttl", 500) - val cloudEvent: CloudEvent = builder.build() - assertTrue(UCloudEvent.isExpiredByCloudEventCreationDate(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent is not expired using creation date when configured ttl is 500 milliseconds " + "with creation date of tomorrow.") - fun test_cloudevent_is_not_expired_cd_when_ttl_500_mili_with_creation_date_of_tomorrow() { - val tomorrow: OffsetDateTime = OffsetDateTime.now().plus(1, ChronoUnit.DAYS) - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withTime(tomorrow).withExtension("ttl", 500) - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isExpiredByCloudEventCreationDate(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent is not expired when no ttl is configured.") - fun test_cloudevent_is_not_expired_when_no_ttl_configured() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withoutExtension("ttl").withId(strUuid) - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isExpired(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent is not expired when configured ttl is zero.") - fun test_cloudevent_is_not_expired_when_ttl_is_zero() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withExtension("ttl", 0).withId(strUuid) - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isExpired(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent is not expired when configured ttl is minus one.") - fun test_cloudevent_is_not_expired_when_ttl_is_minus_one() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withExtension("ttl", -1).withId(strUuid) - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isExpired(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent is not expired when configured ttl is large number.") - fun test_cloudevent_is_not_expired_when_ttl_is_large_number_mili() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withExtension("ttl", Integer.MAX_VALUE).withId(strUuid) - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isExpired(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent is expired when configured ttl is 1 milliseconds.") - @Throws( - InterruptedException::class - ) - fun test_cloudevent_is_expired_when_ttl_1_mili() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withExtension("ttl", 1).withId(strUuid) - val cloudEvent: CloudEvent = builder.build() - Thread.sleep(800) - assertTrue(UCloudEvent.isExpired(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent isExpired when passed invalid UUID") - fun test_cloudevent_is_expired_for_invalid_uuid() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withExtension("ttl", 50000).withId("") - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isExpired(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent has a UUIDV8 id.") - fun test_cloudevent_has_a_UUIDV8_id() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - val cloudEvent: CloudEvent = builder.build() - assertTrue(UCloudEvent.isCloudEventId(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent does not have a UUIDV8 id.") - fun test_cloudevent_does_not_have_a_UUIDV8_id() { - val uuidJava: java.util.UUID = java.util.UUID.randomUUID() - val uuid: UUID = uUID { - msb = uuidJava.mostSignificantBits - lsb = uuidJava.leastSignificantBits - } - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withExtension("ttl", 3).withId(strUuid) - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isCloudEventId(cloudEvent)) - } - - @Test - @DisplayName("Test if the CloudEvent does not have a valid UUID id but some string") - fun test_cloudevent_does_not_have_a_UUID_id_just_some_string() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withExtension("ttl", 3) - val cloudEvent: CloudEvent = builder.build() - assertFalse(UCloudEvent.isCloudEventId(cloudEvent)) - } - - @Test - @DisplayName("Test extract payload from cloud event as Any protobuf object") - fun test_extractPayload_from_cloud_event_as_any_proto_object() { - val payloadForCloudEvent = buildProtoPayloadForTest() - val cloudEventData: ByteArray = payloadForCloudEvent.toByteArray() - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1().withId("someId").withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left#Door")).withDataContentType(DATA_CONTENT_TYPE) - .withDataSchema(URI.create(payloadForCloudEvent.typeUrl)).withData(cloudEventData) - val cloudEvent: CloudEvent = cloudEventBuilder.build() - val extracted: Any = UCloudEvent.getPayload(cloudEvent) - assertEquals(payloadForCloudEvent, extracted) - } - - @Test - @DisplayName("Test extract payload from cloud event when payload is not an Any protobuf object") - @Throws( - InvalidProtocolBufferException::class - ) - fun test_extractPayload_from_cloud_event_when_payload_is_not_an_any_proto_object() { - val payloadForCloudEvent: io.cloudevents.v1.proto.CloudEvent = buildProtoPayloadForTest1() - val cloudEventData: ByteArray = payloadForCloudEvent.toByteArray() - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1().withId("someId").withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left#Door")).withDataContentType(DATA_CONTENT_TYPE) - .withDataSchema(URI.create("type.googleapis.com/io.cloudevents.v1.CloudEvent")).withData(cloudEventData) - val cloudEvent: CloudEvent = cloudEventBuilder.build() - val data: CloudEventData? = cloudEvent.data - val dataAsAny: Any = Any.parseFrom(data?.toBytes()) - val extracted: Any = UCloudEvent.getPayload(cloudEvent) - assertEquals(dataAsAny, extracted) - } - - @Test - @DisplayName("Test extract payload from cloud event when payload is a bad protobuf object") - fun test_extractPayload_from_cloud_event_when_payload_is_bad_proto_object() { - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1().withId("someId").withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left#Door")).withDataContentType(DATA_CONTENT_TYPE) - .withDataSchema(URI.create("type.googleapis.com/io.cloudevents.v1.CloudEvent")) - .withData("

Hello

".toByteArray()) - val cloudEvent: CloudEvent = cloudEventBuilder.build() - val extracted: Any = UCloudEvent.getPayload(cloudEvent) - assertEquals(Any.getDefaultInstance(), extracted) - } - - @Test - @DisplayName("Test extract payload from cloud event as Any protobuf object when there is no data schema") - fun test_extractPayload_from_cloud_event_as_any_proto_object_when_no_schema() { - val payloadForCloudEvent = buildProtoPayloadForTest() - val cloudEventData: ByteArray = payloadForCloudEvent.toByteArray() - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1().withId("someId").withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left#Door")).withDataContentType(DATA_CONTENT_TYPE) - .withData(cloudEventData) - val cloudEvent: CloudEvent = cloudEventBuilder.build() - val extracted: Any = UCloudEvent.getPayload(cloudEvent) - assertEquals(payloadForCloudEvent, extracted) - } - - @Test - @DisplayName("Test extract payload from cloud event as Any protobuf object when there is no data") - fun test_extractPayload_from_cloud_event_as_any_proto_object_when_no_data() { - val payloadForCloudEvent = buildProtoPayloadForTest() - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1().withId("someId").withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left#Door")).withDataContentType(DATA_CONTENT_TYPE) - .withDataSchema(URI.create(payloadForCloudEvent.typeUrl)) - val cloudEvent: CloudEvent = cloudEventBuilder.build() - val extracted: Any = UCloudEvent.getPayload(cloudEvent) - assertEquals(Any.getDefaultInstance(), extracted) - } - - @Test - @DisplayName("Test unpack payload by class from cloud event as protobuf Message object") - fun test_unpack_payload_by_class_from_cloud_event_proto_message_object() { - val payloadForCloudEvent: Any = Any.pack( - io.cloudevents.v1.proto.CloudEvent.newBuilder().setSpecVersion("1.0").setId("hello") - .setSource("//VCU.MY_CAR_VIN/someService").setType("example.demo") - .setProtoData(Any.newBuilder().build()).build() - ) - val cloudEventData: ByteArray = payloadForCloudEvent.toByteArray() - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1().withId("someId").withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left#Door")).withDataContentType(DATA_CONTENT_TYPE) - .withDataSchema(URI.create(payloadForCloudEvent.typeUrl)).withData(cloudEventData) - val cloudEvent: CloudEvent = cloudEventBuilder.build() - val unpackedCE: io.cloudevents.v1.proto.CloudEvent = UCloudEvent.unpack( - cloudEvent, io.cloudevents.v1.proto.CloudEvent::class.java - ) ?: fail() - assertEquals("1.0", unpackedCE.specVersion) - assertEquals("hello", unpackedCE.id) - assertEquals("example.demo", unpackedCE.type) - assertEquals("//VCU.MY_CAR_VIN/someService", unpackedCE.source) - } - - @Test - @DisplayName("Test unpack payload by class from cloud event when protobuf Message is not unpack-able") - fun test_unpack_payload_by_class_from_cloud_event_proto_message_object_when_not_valid_getMessage() { - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1().withId("someId").withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left#Door")).withDataContentType(DATA_CONTENT_TYPE) - .withDataSchema(URI.create("type.googleapis.com/io.cloudevents.v1.CloudEvent")) - .withData("

Hello

".toByteArray()) - val cloudEvent: CloudEvent = cloudEventBuilder.build() - val extracted: io.cloudevents.v1.proto.CloudEvent? = UCloudEvent.unpack( - cloudEvent, io.cloudevents.v1.proto.CloudEvent::class.java - ) - assertNull(extracted) - } - - @Test - @DisplayName("Test pretty printing a cloud event with a sink") - fun test_pretty_printing_a_cloudevent_with_a_sink() { - val sinkForTest = "//bo.cloud/petapp/1/rpc.response" - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withExtension("sink", URI.create(sinkForTest)) - val cloudEvent: CloudEvent = builder.build() - val prettyPrint: String = UCloudEvent.toString(cloudEvent) - val expected = - ("CloudEvent{id='testme', source='/body.access//door.front_left#Door', " + "sink='//bo" + ".cloud/petapp/1/rpc.response', type='pub.v1'}") - assertEquals(expected, prettyPrint) - } - - @Test - @DisplayName("Test pretty printing a cloud event without a sink") - fun test_pretty_printing_a_cloudevent_without_a_sink() { - val cloudEvent: CloudEvent = buildBaseCloudEventBuilderForTest().build() - val prettyPrint: String = UCloudEvent.toString(cloudEvent) - val expected = "CloudEvent{id='testme', source='/body.access//door.front_left#Door', type='pub.v1'}" - assertEquals(expected, prettyPrint) - } - - @Test - @DisplayName("Test the type for a publish message type") - fun test_type_for_publish() { - val uCloudEventType = UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH) - assertEquals("pub.v1", uCloudEventType) - } - - - @Test - @DisplayName("Test the type for a request RPC message type") - fun test_type_for_request() { - val uCloudEventType = UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_REQUEST) - assertEquals("req.v1", uCloudEventType) - } - - @Test - @DisplayName("Test the type for a response RPC message type") - fun test_type_for_response() { - val uCloudEventType = UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_RESPONSE) - assertEquals("res.v1", uCloudEventType) - } - - @Test - @DisplayName("Test the type for a unspecified message type") - fun test_parse_publish_event_type_from_string() { - val uCloudEventType = UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_UNSPECIFIED) - assertTrue(uCloudEventType.isBlank()) - } - - @Test - fun test_to_message_with_valid_event() { - // Additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - priority = UPriority.UPRIORITY_CS2 - ttl = 3 - } - - // CloudEvent - val cloudEvent = CloudEventFactory.publish( - buildSourceForTest(), buildProtoPayloadForTest(), uCloudEventAttributes - ) - - val uMessage = toMessage(cloudEvent) - assertNotNull(uMessage) - } - - @Test - fun test_from_message_with_valid_message() { - // Additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - priority = UPriority.UPRIORITY_CS2 - ttl = 3 - } - - // CloudEvent - val cloudEvent = CloudEventFactory.publish( - buildSourceForTest(), buildProtoPayloadForTest(), uCloudEventAttributes - ) - val uMessage = toMessage(cloudEvent) - assertNotNull(uMessage) - val cloudEvent1 = UCloudEvent.fromMessage(uMessage) - assertNotNull(cloudEvent1) - assertEquals(cloudEvent, cloudEvent1) - } - - @Test - fun test_to_from_message_from_request_cloudevent() { - // Additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - priority = UPriority.UPRIORITY_CS2 - ttl = 3 - token = "someOAuthToken" - traceparent = "someTraceparent" - } - - // CloudEvent - val cloudEvent = CloudEventFactory.request( - buildSourceForTest(), - ("//bo.cloud/petapp/1/rpc" + ".response").toUri(), - buildProtoPayloadForTest(), - uCloudEventAttributes - ) - val result = toMessage(cloudEvent) - assertNotNull(result) - assertEquals(UCloudEvent.getTtl(cloudEvent), result.attributes.ttl) - assertEquals(UCloudEvent.getToken(cloudEvent), result.attributes.getToken()) - assertEquals( - UCloudEvent.getSink(cloudEvent)?.value, LongUriSerializer.INSTANCE.serialize(result.attributes.sink) - ) - assertEquals(UCloudEvent.getTraceparent(cloudEvent), result.attributes.traceparent) - - assertEquals(UCloudEvent.getPayload(cloudEvent).toByteString(), result.payload.getValue()) - assertEquals( - UCloudEvent.getSource(cloudEvent), LongUriSerializer.INSTANCE.serialize(result.attributes.source) - ) - assertEquals(UCloudEvent.getPriority(cloudEvent), getCePriority(result.attributes.priority)) - val cloudEvent1 = UCloudEvent.fromMessage(result) - assertNotNull(cloudEvent1) - assertEquals(cloudEvent, cloudEvent1) - - } - - @Test - fun test_to_from_message_from_request_cloudevent_without_attributes() { - // Additional attributes - val uCloudEventAttributes = uCloudEventAttributes { } - - // CloudEvent - val cloudEvent = CloudEventFactory.request( - buildSourceForTest(), ("//bo.cloud/petapp/1/rpc.response").toUri(), buildProtoPayloadForTest(), uCloudEventAttributes - ) - val result = toMessage(cloudEvent) - assertNotNull(result) - assertFalse(result.attributes.hasTtl()) - assertEquals( - UCloudEvent.getSink(cloudEvent)?.value, LongUriSerializer.INSTANCE.serialize(result.attributes.sink) - ) - assertEquals(UCloudEvent.getPayload(cloudEvent).toByteString(), result.payload.getValue()) - assertEquals( - UCloudEvent.getSource(cloudEvent), LongUriSerializer.INSTANCE.serialize(result.attributes.source) - ) - assertEquals(result.attributes.getPriority().getNumber(), 0) - assertEquals(cloudEvent, UCloudEvent.fromMessage(result)) - - } - - @Test - fun test_to_from_message_from_response_cloudevent() { - // Additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - priority = UPriority.UPRIORITY_CS2 - ttl = 3 - } - - // CloudEvent - val cloudEvent = CloudEventFactory.response( - buildSourceForTest(), - ("//bo.cloud/petapp/1/rpc" + ".response").toUri(), - LongUuidSerializer.INSTANCE.serialize(UUIDV8()), - buildProtoPayloadForTest(), - uCloudEventAttributes - ) - val result = toMessage(cloudEvent) - assertNotNull(result) - assertEquals( - UCloudEvent.getRequestId(cloudEvent), LongUuidSerializer.INSTANCE.serialize(result.attributes.reqid) - ) - assertEquals(UCloudEvent.getTtl(cloudEvent), result.attributes.ttl) - assertEquals( - UCloudEvent.getSink(cloudEvent)?.value, LongUriSerializer.INSTANCE.serialize(result.attributes.sink) - ) - assertEquals(UCloudEvent.getPayload(cloudEvent).toByteString(), result.payload.getValue()) - assertEquals( - UCloudEvent.getSource(cloudEvent), LongUriSerializer.INSTANCE.serialize(result.attributes.source) - ) - assertEquals(UCloudEvent.getPriority(cloudEvent), getCePriority(result.attributes.priority)) - assertEquals(cloudEvent, UCloudEvent.fromMessage(result)) - - } - - @Test - fun test_umessage_has_platform_error_when_platform_error_exists() { - // Additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - priority = UPriority.UPRIORITY_CS2 - ttl = 3 - } - - val protoPayload = buildProtoPayloadForTest() - val cloudEventBuilder = CloudEventFactory.buildBaseCloudEvent( - LongUuidSerializer.INSTANCE.serialize( - UUIDV8() - ), buildSourceForTest(), protoPayload.toByteArray(), protoPayload.typeUrl, uCloudEventAttributes - ) - cloudEventBuilder.withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - .withExtension("commstatus", UCode.ABORTED_VALUE).withExtension("plevel", 2) - val cloudEvent = cloudEventBuilder.build() - val result = toMessage(cloudEvent) - assertNotNull(result) - assertEquals(UCode.ABORTED, UCloudEvent.getCommunicationStatus(cloudEvent)) - assertEquals(2, result.attributes.permissionLevel) - - val cloudEvent1 = UCloudEvent.fromMessage(result) - assertEquals(cloudEvent, cloudEvent1) - - } - - @Test - fun test_to_from_message_from_cloudevent_with_all_payload_formats() { - // Additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - priority = UPriority.UPRIORITY_CS2 - ttl = 3 - } - val protoPayload = buildProtoPayloadForTest() - val cloudEventBuilder = CloudEventFactory.buildBaseCloudEvent( - LongUuidSerializer.INSTANCE.serialize( - UUIDV8() - ), buildSourceForTest(), protoPayload.toByteArray(), protoPayload.typeUrl, uCloudEventAttributes - ) - cloudEventBuilder.withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - - val cloudEvent = cloudEventBuilder.build() - var result = toMessage(cloudEvent) - assertNotNull(result) - assertEquals(UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY, result.payload.getFormat()) - - val cloudEvent1 = UCloudEvent.fromMessage(result) - assertEquals(cloudEvent, cloudEvent1) - assertNull(cloudEvent1.dataContentType) - - val cloudEvent2 = cloudEventBuilder.withDataContentType("").build() - result = toMessage(cloudEvent2) - assertNotNull(result) - assertEquals(UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY, result.payload.getFormat()) - val cloudEvent3 = UCloudEvent.fromMessage(result) - assertNull(cloudEvent3.dataContentType) - - val cloudEvent4 = cloudEventBuilder.withDataContentType("application/json").build() - result = toMessage(cloudEvent4) - assertNotNull(result) - assertEquals(UPayloadFormat.UPAYLOAD_FORMAT_JSON, result.payload.getFormat()) - val cloudEvent5 = UCloudEvent.fromMessage(result) - assertEquals(cloudEvent4, cloudEvent5) - assertEquals("application/json", cloudEvent5.dataContentType) - - val cloudEvent6 = cloudEventBuilder.withDataContentType("application/octet-stream").build() - result = toMessage(cloudEvent6) - assertNotNull(result) - assertEquals(UPayloadFormat.UPAYLOAD_FORMAT_RAW, result.payload.getFormat()) - val cloudEvent7 = UCloudEvent.fromMessage(result) - assertEquals(cloudEvent6, cloudEvent7) - assertEquals("application/octet-stream", cloudEvent7.dataContentType) - - val cloudEvent8 = cloudEventBuilder.withDataContentType("text/plain").build() - result = toMessage(cloudEvent8) - assertNotNull(result) - assertEquals(UPayloadFormat.UPAYLOAD_FORMAT_TEXT, result.payload.getFormat()) - val cloudEvent9 = UCloudEvent.fromMessage(result) - assertEquals(cloudEvent8, cloudEvent9) - assertEquals("text/plain", cloudEvent9.dataContentType) - - val cloudEvent10 = cloudEventBuilder.withDataContentType("application/x-someip").build() - result = toMessage(cloudEvent10) - assertNotNull(result) - assertEquals(UPayloadFormat.UPAYLOAD_FORMAT_SOMEIP, result.payload.getFormat()) - val cloudEvent11 = UCloudEvent.fromMessage(result) - assertEquals(cloudEvent10, cloudEvent11) - assertEquals("application/x-someip", cloudEvent11.dataContentType) - - val cloudEvent12 = cloudEventBuilder.withDataContentType("application/x-someip_tlv").build() - result = toMessage(cloudEvent12) - assertNotNull(result) - assertEquals(UPayloadFormat.UPAYLOAD_FORMAT_SOMEIP_TLV, result.payload.getFormat()) - val cloudEvent13 = UCloudEvent.fromMessage(result) - assertEquals(cloudEvent12, cloudEvent13) - assertEquals("application/x-someip_tlv", cloudEvent13.dataContentType) - } - - @Test - fun test_to_from_message_from_UCP_cloudevent() { - // Additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - ttl = 3 - } - val protoPayload = buildProtoPayloadForTest() - val cloudEventBuilder = CloudEventFactory.buildBaseCloudEvent( - LongUuidSerializer.INSTANCE.serialize( - UUIDV8() - ), buildSourceForTest(), protoPayload.toByteArray(), protoPayload.typeUrl, uCloudEventAttributes - ) - cloudEventBuilder.withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - cloudEventBuilder.withExtension("priority", "CS4") - val cloudEvent = cloudEventBuilder.build() - - val result = toMessage(cloudEvent) - assertNotNull(result) - assertEquals(getCePriority(UPriority.UPRIORITY_CS4), getCePriority(result.attributes.priority)) - val cloudEvent1 = UCloudEvent.fromMessage(result) - assertEquals(getCePriority(UPriority.UPRIORITY_CS4), UCloudEvent.getPriority(cloudEvent1)) - } - - private fun buildSourceForTest(): Uri { - // source - val uUri: UUri = uUri { - entity = uEntity { name = "body.access" } - resource = uResource { - name = "door" - instance = "front_left" - message = "Door" - } - } - - return LongUriSerializer.INSTANCE.serialize(uUri).toUri() - } - - private fun buildBaseCloudEventBuilderForTest(): CloudEventBuilder { - // source - val source = buildSourceForTest() - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - hash = "somehash" - priority = UPriority.UPRIORITY_CS1 - ttl = 3 - token = "someOAuthToken" - traceparent = "someTraceparent" - } - - // build the cloud event - val cloudEventBuilder: CloudEventBuilder = CloudEventFactory.buildBaseCloudEvent( - "testme", source, protoPayload.toByteArray(), protoPayload.typeUrl, uCloudEventAttributes - ) - cloudEventBuilder.withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - return cloudEventBuilder - } - - private fun buildProtoPayloadForTest(): Any { - return Any.pack(buildProtoPayloadForTest1()) - } - - private fun buildProtoPayloadForTest1(): io.cloudevents.v1.proto.CloudEvent { - return io.cloudevents.v1.proto.CloudEvent.newBuilder().setSpecVersion("1.0").setId("hello") - .setSource("//VCU.MY_CAR_VIN/body.access//door.front_left#Door").setType("example.demo") - .setProtoData(Any.newBuilder().build()).build() - } - - companion object { - private const val DATA_CONTENT_TYPE: String = CloudEventFactory.PROTOBUF_CONTENT_TYPE - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/serialize/Base64ProtobufSerializerTest.kt b/src/test/kotlin/org/eclipse/uprotocol/cloudevent/serialize/Base64ProtobufSerializerTest.kt deleted file mode 100644 index 6078aef..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/serialize/Base64ProtobufSerializerTest.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2023 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package org.eclipse.uprotocol.cloudevent.serialize - -import io.cloudevents.CloudEvent -import io.cloudevents.core.builder.CloudEventBuilder -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import java.net.URI -import org.junit.jupiter.api.Assertions.* - -internal class Base64ProtobufSerializerTest { - @Test - @DisplayName("Test deserialize a byte[] to a String") - fun test_deserialize_bytes_to_string() { - - // build the payload as just another cloud event packed into an Any - val datapayload: CloudEvent = CloudEventBuilder.v1() - .withId("hello") - .withType("example.vertx") - .withSource(URI.create("http://localhost")) - .build() - val bytes: ByteArray = CloudEventSerializers.PROTOBUF.serializer().serialize(datapayload) - val payload: String = Base64ProtobufSerializer.deserialize(bytes) - assertEquals("CgVoZWxsbxIQaHR0cDovL2xvY2FsaG9zdBoDMS4wIg1leGFtcGxlLnZlcnR4", payload) - } - - @Test - @DisplayName("Test deserialize a byte[] to a String when byte[] is null") - fun test_deserialize_bytes_to_string_when_bytes_is_null() { - val payload: String = Base64ProtobufSerializer.deserialize(null) - assertEquals("", payload) - } - - @Test - @DisplayName("Test deserialize a byte[] to a String when byte[] is empty") - fun test_deserialize_bytes_to_string_when_bytes_is_empty() { - val payload: String = Base64ProtobufSerializer.deserialize(ByteArray(0)) - assertEquals("", payload) - } - - @Test - @DisplayName("Test serialize a base64 String to bytes") - fun test_serialize_string_into_bytes() { - val base64String = "CgVoZWxsbxIQaHR0cDovL2xvY2FsaG9zdBoDMS4wIg1leGFtcGxlLnZlcnR4" - val bytes: ByteArray = Base64ProtobufSerializer.serialize(base64String) - val datapayload: CloudEvent = CloudEventBuilder.v1() - .withId("hello") - .withType("example.vertx") - .withSource(URI.create("http://localhost")) - .build() - val ceBytes: ByteArray = CloudEventSerializers.PROTOBUF.serializer().serialize(datapayload) - assertArrayEquals(ceBytes, bytes) - } - - @Test - @DisplayName("Test serialize a base64 String to bytes when string is null") - fun test_serialize_string_into_bytes_when_string_is_null() { - val bytes: ByteArray = Base64ProtobufSerializer.serialize(null) - val ceBytes = ByteArray(0) - assertArrayEquals(ceBytes, bytes) - } - - @Test - @DisplayName("Test serialize a base64 String to bytes when string is empty") - fun test_serialize_string_into_bytes_when_string_is_empty() { - val bytes: ByteArray = Base64ProtobufSerializer.serialize("") - val ceBytes = ByteArray(0) - assertArrayEquals(ceBytes, bytes) - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventToJsonSerializerTest.kt b/src/test/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventToJsonSerializerTest.kt deleted file mode 100644 index b58ada1..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventToJsonSerializerTest.kt +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package org.eclipse.uprotocol.cloudevent.serialize - -import com.google.protobuf.Any -import com.google.protobuf.InvalidProtocolBufferException -import com.google.protobuf.Message -import io.cloudevents.CloudEvent -import io.cloudevents.core.builder.CloudEventBuilder -import org.eclipse.uprotocol.cloudevent.datamodel.UCloudEventAttributes -import org.eclipse.uprotocol.cloudevent.factory.CloudEventFactory -import org.eclipse.uprotocol.cloudevent.factory.UCloudEvent -import org.eclipse.uprotocol.uri.Uri -import org.eclipse.uprotocol.v1.UMessageType -import org.eclipse.uprotocol.v1.UPriority -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import java.net.URI -import java.nio.charset.StandardCharsets -import org.junit.jupiter.api.Assertions.* - - -internal class CloudEventToJsonSerializerTest { - private val serializer: CloudEventSerializer = CloudEventToJsonSerializer() - private val protoContentType: String = CloudEventFactory.PROTOBUF_CONTENT_TYPE - @Test - @DisplayName("Test serialize a CloudEvent to JSON") - fun test_serialize_cloud_event_to_json() { - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // cloudevent - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1() - .withId("hello") - .withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left")) - .withDataContentType(protoContentType) - .withDataSchema(URI.create(protoPayload.typeUrl)) - .withData(protoPayload.toByteArray()) - .withExtension("ttl", 3) - .withExtension("priority", "CS1") - val cloudEvent: CloudEvent = cloudEventBuilder.build() - val bytes: ByteArray = serializer.serialize(cloudEvent) - val jsonString = String(bytes, StandardCharsets.UTF_8) - val expected = - "{\"specversion\":\"1.0\",\"id\":\"hello\",\"source\":\"/body.access/1/door.front_left\",\"type\":\"pub.v1\"," + - "\"datacontenttype\":\"application/x-protobuf\",\"dataschema\":\"type.googleapis.com/io.cloudevents.v1.CloudEvent\"," + - "\"priority\":\"CS1\",\"ttl\":3," + - "\"data_base64\":\"CjB0eXBlLmdvb2dsZWFwaXMuY29tL2lvLmNsb3VkZXZlbnRzLnYxLkNsb3VkRXZlbnQSPQoFaGVsbG8SE2h0dHBzOi8vZXhhbXBsZS5jb20aAzEuMCIMZXhhbXBsZS5kZW1vKgoKA3R0bBIDGgEzQgA=\"}" - assertEquals(expected, jsonString) - } - - @Test - @DisplayName("Test serialize and deserialize a CloudEvent to JSON") - fun test_serialize_and_desirialize_cloud_event_to_json() { - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // cloudevent - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1() - .withId("hello") - .withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left")) - .withDataContentType(protoContentType) - .withDataSchema(URI.create(protoPayload.typeUrl)) - .withData(protoPayload.toByteArray()) - .withExtension("ttl", 3) - .withExtension("priority", "CS1") - val cloudEvent: CloudEvent = cloudEventBuilder.build() - val bytes: ByteArray = serializer.serialize(cloudEvent) - val deserialize: CloudEvent = serializer.deserialize(bytes) - assertEquals(cloudEvent, deserialize) - } - - @Test - @DisplayName("Test serialize 2 different cloud events are not the same serialized elements") - fun test_serialize_two_different_cloud_event_are_not_the_same() { - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // cloudevent - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1() - .withId("hello") - .withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left")) - .withDataContentType(protoContentType) - .withDataSchema(URI.create(protoPayload.typeUrl)) - .withData(protoPayload.toByteArray()) - .withExtension("ttl", 3) - .withExtension("priority", "CS1") - val cloudEvent: CloudEvent = cloudEventBuilder.build() - - // another cloudevent - val anotherCloudEvent: CloudEvent = cloudEventBuilder - .withType("file.v1") - .build() - val bytesCloudEvent: ByteArray = serializer.serialize(cloudEvent) - val bytesAnotherCloudEvent: ByteArray = serializer.serialize(anotherCloudEvent) - assertNotEquals(bytesCloudEvent, bytesAnotherCloudEvent) - } - - @Test - @DisplayName("Test serialize 2 equal cloud events are the same serialized elements") - fun test_serialize_two_same_cloud_event_are_the_same() { - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // cloudevent - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1() - .withId("hello") - .withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left")) - .withDataContentType(protoContentType) - .withDataSchema(URI.create(protoPayload.typeUrl)) - .withData(protoPayload.toByteArray()) - .withExtension("ttl", 3) - .withExtension("priority", "CS1") - val cloudEvent: CloudEvent = cloudEventBuilder.build() - - // another cloudevent - val anotherCloudEvent: CloudEvent = cloudEventBuilder.build() - val bytesCloudEvent: ByteArray = serializer.serialize(cloudEvent) - val bytesAnotherCloudEvent: ByteArray = serializer.serialize(anotherCloudEvent) - assertArrayEquals(bytesCloudEvent, bytesAnotherCloudEvent) - } - - @Test - @DisplayName("test double serialization Protobuf when creating CloudEvent with factory methods") - @Throws( - InvalidProtocolBufferException::class - ) - fun test_double_serialization_protobuf_when_creating_cloud_event_with_factory_methods() { - val serializer: CloudEventSerializer = CloudEventSerializers.JSON.serializer() - val source = Uri("/body.access//door.front_left#Door") - - // fake payload - val protoPayload = buildProtoPayloadForTest1() - - // additional attributes - val uCloudEventAttributes = UCloudEventAttributes.uCloudEventAttributes { - hash = "somehash" - priority = UPriority.UPRIORITY_CS1 - ttl = 3 - token = "someOAuthToken" - } - - // build the cloud event - val cloudEventBuilder: CloudEventBuilder = CloudEventFactory.buildBaseCloudEvent( - "testme", source, - protoPayload.toByteArray(), protoPayload.typeUrl, - uCloudEventAttributes - ) - cloudEventBuilder.withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - val cloudEvent1: CloudEvent = cloudEventBuilder.build() - val bytes1: ByteArray = serializer.serialize(cloudEvent1) - val cloudEvent2: CloudEvent = serializer.deserialize(bytes1) - assertEquals(cloudEvent2, cloudEvent1) - val bytes2: ByteArray = serializer.serialize(cloudEvent2) - assertArrayEquals(bytes1, bytes2) - val cloudEvent3: CloudEvent = serializer.deserialize(bytes2) - val cloudEvent3Payload: Any = UCloudEvent.getPayload(cloudEvent3) - val clazz: Class = io.cloudevents.v1.proto.CloudEvent::class.java - assertEquals(cloudEvent3Payload.unpack(clazz), protoPayload.unpack(clazz)) - assertEquals(cloudEvent2, cloudEvent3) - assertEquals(cloudEvent1, cloudEvent3) - } - - @Test - @DisplayName("test double serialization Json") - @Throws(InvalidProtocolBufferException::class) - fun test_double_serialization_json() { - val serializer: CloudEventSerializer = CloudEventSerializers.JSON.serializer() - val builder: CloudEventBuilder = buildCloudEventForTest() - val cloudEventProto = buildProtoPayloadForTest1() - builder.withDataContentType(protoContentType) - builder.withData(cloudEventProto.toByteArray()) - builder.withDataSchema(URI.create(cloudEventProto.typeUrl)) - val cloudEvent1: CloudEvent = builder.build() - val bytes1: ByteArray = serializer.serialize(cloudEvent1) - val cloudEvent2: CloudEvent = serializer.deserialize(bytes1) - assertEquals(cloudEvent2, cloudEvent1) - val bytes2: ByteArray = serializer.serialize(cloudEvent2) - assertArrayEquals(bytes1, bytes2) - val cloudEvent3: CloudEvent = serializer.deserialize(bytes2) - val cloudEvent3Payload: Any = UCloudEvent.getPayload(cloudEvent3) - val clazz: Class = io.cloudevents.v1.proto.CloudEvent::class.java - assertEquals(cloudEvent3Payload.unpack(clazz), cloudEventProto.unpack(clazz)) - assertEquals(cloudEvent2, cloudEvent3) - assertEquals(cloudEvent1, cloudEvent3) - } - - private fun buildCloudEventForTest(): CloudEventBuilder { - return CloudEventBuilder.v1() - .withId("hello") - .withType("pub.v1") - .withSource(URI.create("//VCU.VIN/body.access")) - } - - private fun buildProtoPayloadForTest1(): Any { - val cloudEventProto: io.cloudevents.v1.proto.CloudEvent = io.cloudevents.v1.proto.CloudEvent.newBuilder() - .setSpecVersion("1.0") - .setId("hello") - .setSource("//VCU.VIN/body.access") - .setType("pub.v1") - .setProtoData(Any.newBuilder().build()) - .build() - return Any.pack(cloudEventProto) - } - - private fun buildProtoPayloadForTest(): Any { - val cloudEventProto: io.cloudevents.v1.proto.CloudEvent = io.cloudevents.v1.proto.CloudEvent.newBuilder() - .setSpecVersion("1.0") - .setId("hello") - .setSource("https://example.com") - .setType("example.demo") - .setProtoData(Any.newBuilder().build()) - .putAttributes( - "ttl", io.cloudevents.v1.proto.CloudEvent.CloudEventAttributeValue.newBuilder() - .setCeString("3").build() - ) - .build() - return Any.pack(cloudEventProto) - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventToProtobufSerializerTest.kt b/src/test/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventToProtobufSerializerTest.kt deleted file mode 100644 index 66b9b23..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/serialize/CloudEventToProtobufSerializerTest.kt +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package org.eclipse.uprotocol.cloudevent.serialize - -import com.google.protobuf.Any -import com.google.protobuf.InvalidProtocolBufferException -import com.google.protobuf.Message -import io.cloudevents.CloudEvent -import io.cloudevents.core.builder.CloudEventBuilder -import org.eclipse.uprotocol.cloudevent.datamodel.UCloudEventAttributes -import org.eclipse.uprotocol.cloudevent.factory.CloudEventFactory -import org.eclipse.uprotocol.cloudevent.factory.UCloudEvent -import org.eclipse.uprotocol.uri.Uri -import org.eclipse.uprotocol.uri.serializer.LongUriSerializer -import org.eclipse.uprotocol.uri.toUri -import org.eclipse.uprotocol.v1.* -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import java.net.URI -import java.util.* - - -internal class CloudEventToProtobufSerializerTest { - private val serializer: CloudEventSerializer = CloudEventToProtobufSerializer() - private val protoContentType: String = CloudEventFactory.PROTOBUF_CONTENT_TYPE - - @Test - @DisplayName("Test serialize and deserialize a CloudEvent to protobuf") - fun test_serialize_and_desirialize_cloud_event_to_protobuf() { - - // build the source - val source = buildUriForTest() - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // configure cloud event - val uCloudEventAttributes = UCloudEventAttributes.uCloudEventAttributes { - hash = "somehash" - priority = UPriority.UPRIORITY_CS0 - ttl = 3 - token = "someOAuthToken" - } - val cloudEventBuilder: CloudEventBuilder = CloudEventFactory.buildBaseCloudEvent( - "hello", source, protoPayload.toByteArray(), protoPayload.typeUrl, uCloudEventAttributes - ) - cloudEventBuilder.withType("pub.v1") - val cloudEvent: CloudEvent = cloudEventBuilder.build() - val bytes: ByteArray = serializer.serialize(cloudEvent) - val deserialize: CloudEvent = serializer.deserialize(bytes) - - // data is not the same type, does not work -> expected data=BytesCloudEventData actual data=io.cloudevents.protobuf.ProtoDataWrapper - //assertEquals(cloudEvent, deserialize); - assertCloudEventsAreTheSame(cloudEvent, deserialize) - } - - @Test - @DisplayName("Test serialize 2 different cloud events are not the same serialized elements") - fun test_serialize_two_different_cloud_event_are_not_the_same() { - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // cloudevent - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1().withId("hello").withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left")).withDataContentType("application/protobuf") - .withDataSchema(URI.create(protoPayload.typeUrl)).withData(protoPayload.toByteArray()) - val cloudEvent: CloudEvent = cloudEventBuilder.build() - - // another cloudevent - val anotherCloudEvent: CloudEvent = cloudEventBuilder.withType("file.v1").build() - val bytesCloudEvent: ByteArray = serializer.serialize(cloudEvent) - val bytesAnotherCloudEvent: ByteArray = serializer.serialize(anotherCloudEvent) - assertNotEquals(bytesCloudEvent, bytesAnotherCloudEvent) - } - - @Test - @DisplayName("Test serialize 2 equal cloud events are the same serialized elements") - fun test_serialize_two_same_cloud_event_are_the_same() { - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // cloudevent - val cloudEventBuilder: CloudEventBuilder = CloudEventBuilder.v1().withId("hello").withType("pub.v1") - .withSource(URI.create("/body.access/1/door.front_left")).withDataContentType("application/protobuf") - .withDataSchema(URI.create(protoPayload.typeUrl)).withData(protoPayload.toByteArray()) - val cloudEvent: CloudEvent = cloudEventBuilder.build() - - // another cloudevent - val anotherCloudEvent: CloudEvent = cloudEventBuilder.build() - val bytesCloudEvent: ByteArray = serializer.serialize(cloudEvent) - val bytesAnotherCloudEvent: ByteArray = serializer.serialize(anotherCloudEvent) - assertArrayEquals(bytesCloudEvent, bytesAnotherCloudEvent) - } - - @Test - @DisplayName("test double serialization Protobuf when creating CloudEvent with factory methods") - @Throws( - InvalidProtocolBufferException::class - ) - fun test_double_serialization_protobuf_when_creating_cloud_event_with_factory_methods() { - val serializer: CloudEventSerializer = CloudEventSerializers.PROTOBUF.serializer() - - // source - val source = buildUriForTest() - - // fake payload - val protoPayload = buildProtoPayloadForTest1() - - // additional attributes - val uCloudEventAttributes = UCloudEventAttributes.uCloudEventAttributes { - hash = "somehash" - priority = UPriority.UPRIORITY_CS1 - ttl = 3 - token = "someOAuthToken" - } - - // build the cloud event - val cloudEventBuilder: CloudEventBuilder = CloudEventFactory.buildBaseCloudEvent( - "testme", source, protoPayload.toByteArray(), protoPayload.typeUrl, uCloudEventAttributes - ) - cloudEventBuilder.withType(UCloudEvent.getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - val cloudEvent1: CloudEvent = cloudEventBuilder.build() - val bytes1: ByteArray = serializer.serialize(cloudEvent1) - val cloudEvent2: CloudEvent = serializer.deserialize(bytes1) - assertCloudEventsAreTheSame(cloudEvent2, cloudEvent1) - val bytes2: ByteArray = serializer.serialize(cloudEvent2) - assertArrayEquals(bytes1, bytes2) - val cloudEvent3: CloudEvent = serializer.deserialize(bytes2) - val cloudEvent3Payload: Any = UCloudEvent.getPayload(cloudEvent3) - val clazz: Class = io.cloudevents.v1.proto.CloudEvent::class.java - assertEquals(cloudEvent3Payload.unpack(clazz), protoPayload.unpack(clazz)) - assertEquals(cloudEvent2, cloudEvent3) - assertCloudEventsAreTheSame(cloudEvent1, cloudEvent3) - } - - @Test - @DisplayName("test double serialization Protobuf") - @Throws(InvalidProtocolBufferException::class) - fun test_double_serialization_protobuf() { - val serializer: CloudEventSerializer = CloudEventSerializers.PROTOBUF.serializer() - val builder: CloudEventBuilder = buildCloudEventForTest() - val cloudEventProto = buildProtoPayloadForTest1() - builder.withDataContentType(protoContentType) - builder.withData(cloudEventProto.toByteArray()) - builder.withDataSchema(URI.create(cloudEventProto.typeUrl)) - val cloudEvent1: CloudEvent = builder.build() - val bytes1: ByteArray = serializer.serialize(cloudEvent1) - val cloudEvent2: CloudEvent = serializer.deserialize(bytes1) - assertCloudEventsAreTheSame(cloudEvent2, cloudEvent1) - val bytes2: ByteArray = serializer.serialize(cloudEvent2) - assertArrayEquals(bytes1, bytes2) - val cloudEvent3: CloudEvent = serializer.deserialize(bytes2) - val cloudEvent3Payload: Any = UCloudEvent.getPayload(cloudEvent3) - val clazz: Class = io.cloudevents.v1.proto.CloudEvent::class.java - assertEquals(cloudEvent3Payload.unpack(clazz), cloudEventProto.unpack(clazz)) - assertEquals(cloudEvent2, cloudEvent3) - assertCloudEventsAreTheSame(cloudEvent1, cloudEvent3) - } - - @Test - @DisplayName("test double serialization proto to Json") - fun test_double_serialization_proto_to_json() { - val protoSerializer: CloudEventSerializer = CloudEventSerializers.PROTOBUF.serializer() - val jsonSerializer: CloudEventSerializer = CloudEventSerializers.JSON.serializer() - val builder: CloudEventBuilder = buildCloudEventForTest() - val cloudEventProto = buildProtoPayloadForTest1() - builder.withDataContentType(protoContentType) - builder.withData(cloudEventProto.toByteArray()) - builder.withDataSchema(URI.create(cloudEventProto.typeUrl)) - val cloudEvent1: CloudEvent = builder.build() - val bytes1: ByteArray = protoSerializer.serialize(cloudEvent1) - val cloudEvent2: CloudEvent = protoSerializer.deserialize(bytes1) - assertCloudEventsAreTheSame(cloudEvent2, cloudEvent1) - val bytes2: ByteArray = protoSerializer.serialize(cloudEvent2) - assertArrayEquals(bytes1, bytes2) - val bytes3: ByteArray = jsonSerializer.serialize(cloudEvent2) - val cloudEvent3: CloudEvent = jsonSerializer.deserialize(bytes3) - assertCloudEventsAreTheSame(cloudEvent2, cloudEvent3) - assertEquals(cloudEvent1, cloudEvent3) - } - - @Test - @DisplayName("test double serialization json to proto") - fun test_double_serialization_json_to_proto() { - val protoSerializer: CloudEventSerializer = CloudEventSerializers.PROTOBUF.serializer() - val jsonSerializer: CloudEventSerializer = CloudEventSerializers.JSON.serializer() - val builder: CloudEventBuilder = buildCloudEventForTest() - val cloudEventProto = buildProtoPayloadForTest1() - builder.withDataContentType(protoContentType) - builder.withData(cloudEventProto.toByteArray()) - builder.withDataSchema(URI.create(cloudEventProto.typeUrl)) - val cloudEvent1: CloudEvent = builder.build() - val bytes1: ByteArray = jsonSerializer.serialize(cloudEvent1) - val cloudEvent2: CloudEvent = jsonSerializer.deserialize(bytes1) - assertEquals(cloudEvent2, cloudEvent1) - val bytes2: ByteArray = jsonSerializer.serialize(cloudEvent2) - assertArrayEquals(bytes1, bytes2) - val bytes3: ByteArray = protoSerializer.serialize(cloudEvent2) - val cloudEvent3: CloudEvent = protoSerializer.deserialize(bytes3) - assertCloudEventsAreTheSame(cloudEvent2, cloudEvent3) - assertCloudEventsAreTheSame(cloudEvent1, cloudEvent3) - } - - private fun assertCloudEventsAreTheSame(cloudEvent1: CloudEvent, cloudEvent2: CloudEvent) { - assertNotNull(cloudEvent1) - assertNotNull(cloudEvent2) - assertEquals(cloudEvent1.specVersion.toString(), cloudEvent2.specVersion.toString()) - assertEquals(cloudEvent1.id, cloudEvent2.id) - assertEquals(cloudEvent1.source, cloudEvent2.source) - assertEquals(cloudEvent1.type, cloudEvent2.type) - assertEquals(cloudEvent1.dataContentType, cloudEvent2.dataContentType) - assertEquals(cloudEvent1.dataSchema, cloudEvent2.dataSchema) - val ce1ExtensionNames: Set = cloudEvent1.extensionNames - val ce2ExtensionNames: Set = cloudEvent2.extensionNames - assertEquals(ce1ExtensionNames.joinToString(","), ce2ExtensionNames.joinToString(",")) - assertArrayEquals( - Objects.requireNonNull(cloudEvent1.data).toBytes(), Objects.requireNonNull(cloudEvent2.data).toBytes() - ) - assertEquals(cloudEvent1, cloudEvent2) - } - - private fun buildCloudEventForTest(): CloudEventBuilder { - return CloudEventBuilder.v1().withId("hello").withType("pub.v1").withSource(URI.create("//VCU.VIN/body.access")) - } - - private fun buildProtoPayloadForTest1(): Any { - val cloudEventProto: io.cloudevents.v1.proto.CloudEvent = - io.cloudevents.v1.proto.CloudEvent.newBuilder().setSpecVersion("1.0").setId("hello") - .setSource("//VCU.VIN/body.access").setType("pub.v1").setProtoData(Any.newBuilder().build()).build() - return Any.pack(cloudEventProto) - } - - private fun buildUriForTest(): Uri { - val uri: UUri = uUri { - entity = uEntity { name = "body.access" } - resource = uResource { - name = "door" - instance = "front_left" - message = "Door" - } - } - - - return LongUriSerializer.INSTANCE.serialize(uri).toUri() - } - - private fun buildProtoPayloadForTest(): Any { - val cloudEventProto: io.cloudevents.v1.proto.CloudEvent = - io.cloudevents.v1.proto.CloudEvent.newBuilder().setSpecVersion("1.0").setId("hello") - .setSource("https://example.com").setType("example.demo").setProtoData(Any.newBuilder().build()) - .putAttributes( - "ttl", - io.cloudevents.v1.proto.CloudEvent.CloudEventAttributeValue.newBuilder().setCeString("3").build() - ).build() - return Any.pack(cloudEventProto) - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/validate/CloudEventValidatorTest.kt b/src/test/kotlin/org/eclipse/uprotocol/cloudevent/validate/CloudEventValidatorTest.kt deleted file mode 100644 index 5f3f75b..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/validate/CloudEventValidatorTest.kt +++ /dev/null @@ -1,646 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package org.eclipse.uprotocol.cloudevent.validate - -import com.google.protobuf.Any -import io.cloudevents.CloudEvent -import io.cloudevents.core.builder.CloudEventBuilder -import org.eclipse.uprotocol.cloudevent.datamodel.UCloudEventAttributes.Companion.uCloudEventAttributes -import org.eclipse.uprotocol.cloudevent.factory.CloudEventFactory -import org.eclipse.uprotocol.cloudevent.factory.UCloudEvent -import org.eclipse.uprotocol.cloudevent.factory.UCloudEvent.getEventType -import org.eclipse.uprotocol.cloudevent.validate.CloudEventValidator.Companion.getValidator -import org.eclipse.uprotocol.uri.Uri -import org.eclipse.uprotocol.uri.serializer.LongUriSerializer -import org.eclipse.uprotocol.uri.toUri -import org.eclipse.uprotocol.uuid.factory.UUIDV6 -import org.eclipse.uprotocol.uuid.factory.UUIDV8 -import org.eclipse.uprotocol.uuid.serializer.LongUuidSerializer -import org.eclipse.uprotocol.v1.* -import org.eclipse.uprotocol.validation.ValidationResult -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import java.net.URI -import java.time.Instant - - -internal class CloudEventValidatorTest { - @Test - @DisplayName("Test get a publish cloud event validator") - fun test_get_a_publish_cloud_event_validator() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withType("pub.v1") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = cloudEvent.getValidator() - val status: UStatus = validator.validateType(cloudEvent).toStatus() - assertEquals(status, ValidationResult.STATUS_SUCCESS) - assertEquals("CloudEventValidator.Publish", validator.toString()) - } - - @Test - @DisplayName("Test get a notification cloud event validator") - fun test_get_a_notification_cloud_event_validator() { - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withExtension("sink", "//bo.cloud/petapp").withType("not.v1") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Notification - val status: UStatus = validator.validateType(cloudEvent).toStatus() - assertEquals(status, ValidationResult.STATUS_SUCCESS) - assertEquals("CloudEventValidator.Notification", validator.toString()) - } - - @Test - @DisplayName("Test publish cloud event type") - fun test_publish_cloud_event_type() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withType("res.v1") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Publish - val status: UStatus = validator.validateType(cloudEvent).toStatus() - assertEquals(UCode.INVALID_ARGUMENT, status.code) - assertEquals( - "Invalid CloudEvent type [res.v1]. CloudEvent of type Publish must have a type of 'pub.v1'", status.message - ) - } - - @Test - @DisplayName("Test notification cloud event type") - fun test_notification_cloud_event_type() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withType("res.v1") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Notification - val status: UStatus = validator.validateType(cloudEvent).toStatus() - assertEquals(UCode.INVALID_ARGUMENT, status.code) - assertEquals( - "Invalid CloudEvent type [res.v1]. CloudEvent of type Notification must have a type of 'not.v1'", status.message - ) - } - - @Test - @DisplayName("Test get a request cloud event validator") - fun test_get_a_request_cloud_event_validator() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withType("req.v1") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = cloudEvent.getValidator() - val status: UStatus = validator.validateType(cloudEvent).toStatus() - assertEquals(status, ValidationResult.STATUS_SUCCESS) - assertEquals("CloudEventValidator.Request", validator.toString()) - } - - @Test - @DisplayName("Test request cloud event type") - fun test_request_cloud_event_type() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withType("pub.v1") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Request - val status: UStatus = validator.validateType(cloudEvent).toStatus() - assertEquals(UCode.INVALID_ARGUMENT, status.code) - assertEquals( - "Invalid CloudEvent type [pub.v1]. CloudEvent of type Request must have a type of 'req.v1'", status.message - ) - } - - @Test - @DisplayName("Test get a response cloud event validator") - fun test_get_a_response_cloud_event_validator() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withType("res.v1") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = cloudEvent.getValidator() - val status: UStatus = validator.validateType(cloudEvent).toStatus() - assertEquals(status, ValidationResult.STATUS_SUCCESS) - assertEquals("CloudEventValidator.Response", validator.toString()) - } - - @Test - @DisplayName("Test response cloud event type") - fun test_response_cloud_event_type() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withType("pub.v1") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Response - val status: UStatus = validator.validateType(cloudEvent).toStatus() - assertEquals(UCode.INVALID_ARGUMENT, status.code) - assertEquals( - "Invalid CloudEvent type [pub.v1]. CloudEvent of type Response must have a type of 'res.v1'", status.message - ) - } - - @Test - @DisplayName("Test get a publish cloud event validator when cloud event type is unknown") - fun test_get_a_publish_cloud_event_validator_when_cloud_event_type_is_unknown() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withType("lala.v1") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = cloudEvent.getValidator() - assertEquals("CloudEventValidator.Publish", validator.toString()) - } - - @Test - @DisplayName("Test validate version") - fun validate_cloud_event_version_when_valid() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - .withId(strUuid) - val cloudEvent: CloudEvent = builder.build() - val status: UStatus = CloudEventValidator.validateVersion(cloudEvent).toStatus() - assertEquals(status, ValidationResult.STATUS_SUCCESS) - } - - @Test - @DisplayName("Test validate version when not valid") - fun validate_cloud_event_version_when_not_valid() { - val payloadForTest = buildProtoPayloadForTest() - val builder: CloudEventBuilder = - CloudEventBuilder.v03().withId("id").withType("pub.v1").withSource(URI.create("/body.access")) - .withDataContentType("application/protobuf").withDataSchema(URI.create(payloadForTest.typeUrl)) - .withData(payloadForTest.toByteArray()) - val cloudEvent: CloudEvent = builder.build() - val status: UStatus = CloudEventValidator.validateVersion(cloudEvent).toStatus() - assertEquals(UCode.INVALID_ARGUMENT, status.code) - assertEquals("Invalid CloudEvent version [0.3]. CloudEvent version must be 1.0.", status.message) - } - - @Test - @DisplayName("Test validate cloudevent id when valid") - fun validate_cloud_event_id_when_valid() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - .withId(strUuid) - val cloudEvent: CloudEvent = builder.build() - val status: UStatus = CloudEventValidator.validateId(cloudEvent).toStatus() - assertEquals(status, ValidationResult.STATUS_SUCCESS) - } - - @Test - @DisplayName("Test validate cloudevent id when not UUIDv8 type id") - fun validate_cloud_event_id_when_not_uuidv6_type_id() { - val uuidJava: java.util.UUID = java.util.UUID.randomUUID() - val uuid: UUID = uUID { - msb = uuidJava.mostSignificantBits - lsb = uuidJava.leastSignificantBits - } - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - .withId(strUuid) - val cloudEvent: CloudEvent = builder.build() - val status: UStatus = CloudEventValidator.validateId(cloudEvent).toStatus() - assertEquals(UCode.INVALID_ARGUMENT, status.code) - assertEquals( - "Invalid CloudEvent Id [$strUuid]. CloudEvent Id must be of type UUIDv8.", status.message - ) - } - - @Test - @DisplayName("Test validate cloudevent id when not valid") - fun validate_cloud_event_id_when_not_valid() { - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - .withId("testme") - val cloudEvent: CloudEvent = builder.build() - val status: UStatus = CloudEventValidator.validateId(cloudEvent).toStatus() - assertEquals(UCode.INVALID_ARGUMENT, status.code) - assertEquals("Invalid CloudEvent Id [testme]. CloudEvent Id must be of type UUIDv8.", status.message) - } - - @Test - @DisplayName("Test local Publish type CloudEvent is valid everything is valid") - fun test_publish_type_cloudevent_is_valid_when_everything_is_valid_local() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("/body.access/1/door.front_left#Door")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Publish - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals(ValidationResult.success(), result) - } - - @Test - @DisplayName("Test microRemote Publish type CloudEvent is valid everything is valid") - fun test_publish_type_cloudevent_is_valid_when_everything_is_valid_remote() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("//VCU.myvin/body.access/1/door.front_left#Door")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Publish - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals(ValidationResult.success(), result) - } - - @Test - @DisplayName("Test microRemote Publish type CloudEvent is valid everything is valid with a sink") - fun test_publish_type_cloudevent_is_valid_when_everything_is_valid_remote_with_a_sink() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("//VCU.myvin/body.access/1/door.front_left#Door")) - .withExtension("sink", "//bo.cloud/petapp") - .withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Publish - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals(ValidationResult.success(), result) - } - - @Test - @DisplayName("Test microRemote Publish type CloudEvent is not valid everything is valid with invalid sink") - fun test_publish_type_cloudevent_is_not_valid_when_remote_with_invalid_sink() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("//VCU.myvin/body.access/1/door.front_left#Door")) - .withExtension("sink", "//bo.cloud").withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Publish - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals( - "Invalid CloudEvent sink [//bo.cloud]. Uri is missing uSoftware Entity name.", result.getMessage() - ) - } - - @Test - @DisplayName("Test Publish type CloudEvent is not valid when source is empty") - fun test_publish_type_cloudevent_is_not_valid_when_source_is_empty() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid).withSource(URI.create("/")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Publish - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals("Invalid Publish type CloudEvent source [/]. Uri is empty.", result.getMessage()) - } - - @Test - @DisplayName("Test Notification type CloudEvent is not valid when source is empty") - fun test_notification_type_cloudevent_is_not_valid_when_source_is_empty() { - val strUUID: String = LongUuidSerializer.INSTANCE.serialize(UUIDV8()) - val cloudEvent = buildBaseCloudEventBuilderForTest().withId(strUUID) - .withSource(URI.create("/")).withType(getEventType(UMessageType.UMESSAGE_TYPE_NOTIFICATION)).build() - val validator: CloudEventValidator = Notification - val result = validator.validate(cloudEvent) - assertEquals( - "Invalid Notification type CloudEvent source [/]. Uri is empty.,Invalid CloudEvent sink. Notification CloudEvent sink must be an uri.", - result.getMessage() - ) - } - - @Test - @DisplayName("Test Publish type CloudEvent is not valid when source is invalid and id invalid") - fun test_publish_type_cloudevent_is_not_valid_when_source_is_missing_authority() { - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withId("testme").withSource(URI.create("/body.access")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Publish - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals( - "Invalid CloudEvent Id [testme]. CloudEvent Id must be of type UUIDv8.," + "Invalid Publish type " + "CloudEvent source [/body.access]. UriPart is missing uResource name.", - result.getMessage() - ) - } - - @Test - @DisplayName("Test Publish type CloudEvent is not valid when source is invalid missing message information") - fun test_publish_type_cloudevent_is_not_valid_when_source_is_missing_message_info() { - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId("testme") - .withSource(URI.create("/body.access/1/door.front_left")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Publish - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals( - "Invalid CloudEvent Id [testme]. CloudEvent Id must be of type UUIDv8.," + "Invalid Publish type " + "CloudEvent source [/body.access/1/door.front_left]. UriPart is missing Message information.", - result.getMessage() - ) - } - - @Test - @DisplayName("Test Notification type CloudEvent is valid everything is valid") - fun test_notification_type_cloudevent_is_valid_when_everything_is_valid() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("/body.access/1/door.front_left#Door")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_NOTIFICATION)) - .withExtension("sink", "//bo.cloud/petapp") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Notification - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals(ValidationResult.success(), result) - } - - @Test - @DisplayName("Test Notification type CloudEvent is not valid missing sink") - fun test_notification_type_cloudevent_is_not_valid_missing_sink() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("/body.access/1/door.front_left#Door")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_NOTIFICATION)) - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Notification - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals("Invalid CloudEvent sink. Notification CloudEvent sink must be an uri.", result.getMessage()) - } - - @Test - @DisplayName("Test Notification type CloudEvent is not valid invalid sink") - fun test_notification_type_cloudevent_is_not_valid_invalid_sink() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("/body.access/1/door.front_left#Door")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_NOTIFICATION)).withExtension("sink", "//bo.cloud") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Notification - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals( - "Invalid Notification type CloudEvent sink [//bo.cloud]. Uri is missing uSoftware Entity name.", - result.getMessage() - ) - } - - @Test - @DisplayName("Test Request type CloudEvent is valid everything is valid") - fun test_request_type_cloudevent_is_valid_when_everything_is_valid() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("//bo.cloud/petapp//rpc.response")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_REQUEST)) - .withExtension("sink", "//VCU.myvin/body.access/1/rpc.UpdateDoor") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Request - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals(ValidationResult.success(), result) - } - - @Test - @DisplayName("Test Request type CloudEvent is not valid invalid source") - fun test_request_type_cloudevent_is_not_valid_invalid_source() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withId(strUuid).withSource(URI.create("//bo.cloud/petapp//dog")) - .withExtension("sink", "//VCU.myvin/body.access/1/rpc.UpdateDoor") - .withType(getEventType(UMessageType.UMESSAGE_TYPE_REQUEST)) - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Request - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals( - "Invalid RPC Request CloudEvent source [//bo.cloud/petapp//dog]. " + "Invalid RPC uri application " + "response topic. UriPart is missing rpc.response.", - result.getMessage() - ) - } - - @Test - @DisplayName("Test Request type CloudEvent is not valid missing sink") - fun test_request_type_cloudevent_is_not_valid_missing_sink() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("//bo.cloud/petapp//rpc.response")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_REQUEST)) - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Request - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals( - "Invalid RPC Request CloudEvent sink. Request CloudEvent sink must be uri for the method to be called.", - result.getMessage() - ) - } - - @Test - @DisplayName("Test Request type CloudEvent is not valid sink not rpc command") - fun test_request_type_cloudevent_is_not_valid_invalid_sink_not_rpc_command() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("//bo.cloud/petapp//rpc.response")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_REQUEST)) - .withExtension("sink", "//VCU.myvin/body.access/1/UpdateDoor") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Request - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals( - "Invalid RPC Request CloudEvent sink [//VCU.myvin/body.access/1/UpdateDoor]. " + "Invalid RPC method " + "uri. UriPart should be the method to be called, or method from response.", - result.getMessage() - ) - } - - @Test - @DisplayName("Test Response type CloudEvent is valid everything is valid") - fun test_response_type_cloudevent_is_valid_when_everything_is_valid() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("//VCU.myvin/body.access/1/rpc.UpdateDoor")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_RESPONSE)) - .withExtension("sink", "//bo.cloud/petapp//rpc.response") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Response - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals(ValidationResult.success(), result) - } - - @Test - @DisplayName("Test Response type CloudEvent is not valid invalid source") - fun test_response_type_cloudevent_is_not_valid_invalid_source() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("//VCU.myvin/body.access/1/UpdateDoor")) - .withExtension("sink", "//bo.cloud/petapp//rpc.response") - .withType(getEventType(UMessageType.UMESSAGE_TYPE_RESPONSE)) - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Response - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals( - "Invalid RPC Response CloudEvent source [//VCU.myvin/body.access/1/UpdateDoor]. " + "Invalid RPC " + "method uri. UriPart should be the method to be called, or method from response.", - result.getMessage() - ) - } - - @Test - @DisplayName("Test Response type CloudEvent is not valid missing sink and invalid source") - fun test_response_type_cloudevent_is_not_valid_missing_sink_and_invalid_source() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withSource(URI.create("//VCU.myvin/body.access/1/UpdateDoor")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_RESPONSE)) - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Response - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals( - "Invalid RPC Response CloudEvent source [//VCU.myvin/body.access/1/UpdateDoor]. " + "Invalid RPC " + "method uri. UriPart should be the method to be called, or method from response.," + "Invalid" + " CloudEvent sink. Response CloudEvent sink must be uri the destination of the response.", - result.getMessage() - ) - } - - @Test - @DisplayName("Test Response type CloudEvent is not valid sink and source, missing entity name.") - fun test_response_type_cloudevent_is_not_valid_invalid_sink() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = buildBaseCloudEventBuilderForTest().withId(strUuid) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_RESPONSE)) - .withSource(URI.create("//VCU.myvin")).withExtension("sink", "//bo.cloud") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Response - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals( - "Invalid RPC Response CloudEvent source [//VCU.myvin]. Invalid RPC method uri. Uri is missing " + "uSoftware Entity name.,Invalid RPC Response CloudEvent sink [//bo.cloud]. Invalid RPC uri " + "application response topic. Uri is missing uSoftware Entity name.", - result.getMessage() - ) - } - - @Test - @DisplayName("Test Response type CloudEvent is not valid source not rpc command") - fun test_response_type_cloudevent_is_not_valid_invalid_source_not_rpc_command() { - val uuid: UUID = UUIDV8() - val strUuid = LongUuidSerializer.INSTANCE.serialize(uuid) - val builder: CloudEventBuilder = - buildBaseCloudEventBuilderForTest().withId(strUuid).withSource(URI.create("//bo.cloud/petapp/1/dog")) - .withType(getEventType(UMessageType.UMESSAGE_TYPE_RESPONSE)) - .withExtension("sink", "//VCU.myvin/body.access/1/UpdateDoor") - val cloudEvent: CloudEvent = builder.build() - val validator: CloudEventValidator = Response - val result: ValidationResult = validator.validate(cloudEvent) - assertEquals( - "Invalid RPC Response CloudEvent source [//bo.cloud/petapp/1/dog]. Invalid RPC method uri. UriPart " + "should be the method to be called, or method from response.," + "Invalid RPC Response " + "CloudEvent sink [//VCU.myvin/body.access/1/UpdateDoor]. " + "Invalid RPC uri application " + "response topic. UriPart is missing rpc.response.", - result.getMessage() - ) - } - - private fun buildBaseCloudEventBuilderForTest(): CloudEventBuilder { - // source - val source = buildLongUriForTest() - - // fake payload - val protoPayload = buildProtoPayloadForTest() - - // additional attributes - val uCloudEventAttributes = uCloudEventAttributes { - hash = "somehash" - priority = UPriority.UPRIORITY_CS1 - ttl = 3 - token = "someOAuthToken" - } - // build the cloud event - val cloudEventBuilder: CloudEventBuilder = CloudEventFactory.buildBaseCloudEvent( - "testme", source, protoPayload.toByteArray(), protoPayload.typeUrl, uCloudEventAttributes - ) - cloudEventBuilder.withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)) - return cloudEventBuilder - } - - private fun buildProtoPayloadForTest(): Any { - val cloudEventProto: io.cloudevents.v1.proto.CloudEvent = - io.cloudevents.v1.proto.CloudEvent.newBuilder().setSpecVersion("1.0").setId("hello") - .setSource("/body.access").setType("example.demo").setProtoData(Any.newBuilder().build()).build() - return Any.pack(cloudEventProto) - } - - @Test - @DisplayName("Test create a v6 Cloudevent and validate it works with this SDK") - fun test_create_a_v6_cloudevent_and_validate_it_against_sdk() { - - // source - val source = buildLongUriForTest() - val uuid: UUID = UUIDV6() - val id = LongUuidSerializer.INSTANCE.serialize(uuid) - - // fake payload - val protoPayload = buildProtoPayloadForTest() - val attributes = uCloudEventAttributes { - priority = UPriority.UPRIORITY_CS0 - ttl = 1000 - } - - // build the cloud event - val cloudEvent: CloudEvent = CloudEventFactory.buildBaseCloudEvent( - id, source, protoPayload.toByteArray(), protoPayload.typeUrl, attributes - ).withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)).build() - val validator: CloudEventValidator = Publish - val result: ValidationResult = validator.validate(cloudEvent) - assertTrue(result.isSuccess()) - assertFalse(UCloudEvent.isExpired(cloudEvent)) - } - - @Test - @DisplayName("Test create an expired v6 Cloudevent to ensure we report the expiration") - fun test_create_an_expired_v6_cloudevent() { - - // source - val source = buildLongUriForTest() - val uuid: UUID = UUIDV6(Instant.now().minusSeconds(100)) - val id = LongUuidSerializer.INSTANCE.serialize(uuid) - - // fake payload - val protoPayload = buildProtoPayloadForTest() - val attributes = uCloudEventAttributes { - priority = UPriority.UPRIORITY_CS0 - ttl = 1000 - } - - // build the cloud event - val cloudEvent: CloudEvent = CloudEventFactory.buildBaseCloudEvent( - id, source, protoPayload.toByteArray(), protoPayload.typeUrl, attributes - ).withType(getEventType(UMessageType.UMESSAGE_TYPE_PUBLISH)).build() - val validator: CloudEventValidator = Publish - val result: ValidationResult = validator.validate(cloudEvent) - assertTrue(result.isSuccess()) - assertTrue(UCloudEvent.isExpired(cloudEvent)) - } - - @Test - @DisplayName("Test fetching the notification validator") - fun test_fetching_the_notification_validator() { - val cloudEvent = buildBaseCloudEventBuilderForTest().withType(getEventType(UMessageType.UMESSAGE_TYPE_NOTIFICATION)).build() - val validator: CloudEventValidator = cloudEvent.getValidator() - val status = validator.validateType(cloudEvent).toStatus() - assertEquals(status, ValidationResult.STATUS_SUCCESS) - assertEquals("CloudEventValidator.Notification", validator.toString()) - } - - private fun buildLongUriForTest(): Uri { - return LongUriSerializer.INSTANCE.serialize(buildUUriForTest()).toUri() - } - - private fun buildUUriForTest(): UUri { - return uUri { - entity = uEntity { name = "body.access" } - resource = uResource { - name = "door" - instance = "front_left" - message = "Door" - } - } - - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/validate/ValidationResultTest.kt b/src/test/kotlin/org/eclipse/uprotocol/cloudevent/validate/ValidationResultTest.kt deleted file mode 100644 index 7ee5629..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/cloudevent/validate/ValidationResultTest.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2023 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package org.eclipse.uprotocol.cloudevent.validate - - -import org.eclipse.uprotocol.v1.UCode -import org.eclipse.uprotocol.v1.UStatus -import org.eclipse.uprotocol.v1.uStatus -import org.eclipse.uprotocol.validation.ValidationResult -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test - -internal class ValidationResultTest { - @Test - @DisplayName("Test success validation result to string") - fun test_success_validation_result_toString() { - val success: ValidationResult = ValidationResult.success() - assertEquals("ValidationResult.Success()", success.toString()) - } - - @Test - @DisplayName("Test failure validation result to string") - fun test_failure_validation_result_toString() { - val failure: ValidationResult = ValidationResult.failure("boom") - assertEquals("ValidationResult.Failure(message='boom')", failure.toString()) - } - - @Test - @DisplayName("Test success validation result isSuccess") - fun test_success_validation_result_isSuccess() { - val success: ValidationResult = ValidationResult.success() - assertTrue(success.isSuccess()) - } - - @Test - @DisplayName("Test failure validation result isSuccess") - fun test_failure_validation_result_isSuccess() { - val failure: ValidationResult = ValidationResult.failure("boom") - assertFalse(failure.isSuccess()) - } - - @Test - @DisplayName("Test success message") - fun test_success_validation_result_getMessage() { - val success: ValidationResult = ValidationResult.success() - assertTrue(success.getMessage().isBlank()) - } - - @Test - @DisplayName("Test failure message") - fun test_failure_validation_result_getMessage() { - val failure: ValidationResult = ValidationResult.failure("boom") - assertEquals("boom", failure.getMessage()) - } - - @Test - @DisplayName("Test success toStatus") - fun test_success_validation_result_toStatus() { - val success: ValidationResult = ValidationResult.success() - assertEquals(ValidationResult.STATUS_SUCCESS, success.toStatus()) - } - - @Test - @DisplayName("Test failure toStatus") - fun test_failure_validation_result_toStatus() { - val failure: ValidationResult = ValidationResult.failure("boom") - val status: UStatus = uStatus { - code = UCode.INVALID_ARGUMENT - message = "boom" - } - assertEquals(status, failure.toStatus()) - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/rpc/CallOptionsTest.kt b/src/test/kotlin/org/eclipse/uprotocol/rpc/CallOptionsTest.kt deleted file mode 100644 index f153207..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/rpc/CallOptionsTest.kt +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package org.eclipse.uprotocol.rpc - -import org.eclipse.uprotocol.v1.callOptions -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue - -class CallOptionsTest { - @Test - @DisplayName("Make sure the toString works") - fun testToString() { - val callOptions = callOptions { - ttl = 30 - token = "someToken" - } - val expected = "ttl: 30\ntoken: \"someToken\"\n" - assertEquals(expected, callOptions.toString()) - } - - @Test - @DisplayName("Test creating CallOptions with only a token") - fun testCreatingCallOptionsWithAToken() { - val callOptions = callOptions { - token = "someToken" - } - assertTrue(callOptions.token.isNotEmpty()) - assertEquals("someToken", callOptions.token) - } - - - @Test - @DisplayName("Test creating CallOptions with only an empty string token") - fun testCreatingCallOptionsWithAnEmptyStringToken() { - val callOptions = callOptions { - token = "" - } - assertTrue(callOptions.token.isEmpty()) - } - - @Test - @DisplayName("Test creating CallOptions with only a token with only spaces") - fun testCreatingCallOptionsWithATokenWithOnlySpaces() { - val callOptions = callOptions { - token = " " - } - assertTrue(callOptions.token.isBlank()) - } - - @Test - @DisplayName("Test creating CallOptions with only a timeout") - fun testCreatingCallOptionsWithATimeout() { - val callOptions = callOptions { - ttl = 30 - } - assertEquals(30, callOptions.ttl) - assertTrue(callOptions.token.isEmpty()) - } -} diff --git a/src/test/kotlin/org/eclipse/uprotocol/rpc/RpcTest.kt b/src/test/kotlin/org/eclipse/uprotocol/rpc/RpcTest.kt deleted file mode 100644 index 3eb521f..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/rpc/RpcTest.kt +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package org.eclipse.uprotocol.rpc - - -import com.google.protobuf.Any -import com.google.protobuf.Int32Value -import com.google.protobuf.kotlin.toByteString -import io.cloudevents.v1.proto.CloudEvent -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.test.runTest -import org.eclipse.uprotocol.uri.serializer.LongUriSerializer -import org.eclipse.uprotocol.v1.* -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import java.util.concurrent.CompletionException -import kotlin.test.fail - - -internal class RpcTest { - private var returnsNumber3: RpcClient = object : RpcClient { - override fun invokeMethod(methodUri: UUri, requestPayload: UPayload, options: CallOptions): Flow { - return flowOf(uMessage { - payload = uPayload { - format = UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF - value = Any.pack(Int32Value.of(3)).toByteString() - } - }) - } - } - private var happyPath: RpcClient = object : RpcClient { - override fun invokeMethod(methodUri: UUri, requestPayload: UPayload, options: CallOptions): Flow { - return flowOf(testUMessage) - } - } - private var withStatusCodeInsteadOfHappyPath: RpcClient = object : RpcClient { - override fun invokeMethod(methodUri: UUri, requestPayload: UPayload, options: CallOptions): Flow { - val status: UStatus = uStatus { - code = UCode.INVALID_ARGUMENT - message = "boom" - } - val any: Any = Any.pack(status) - return flowOf(uMessage { - payload = uPayload { - format = UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF - value = any.toByteString() - } - }) - } - } - private var withStatusCodeHappyPath: RpcClient = object : RpcClient { - override fun invokeMethod(methodUri: UUri, requestPayload: UPayload, options: CallOptions): Flow { - val status: UStatus = uStatus { - code = UCode.OK - message = "all good" - } - val any: Any = Any.pack(status) - return flowOf(uMessage { - payload = uPayload { - format = UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF - value = any.toByteString() - } - }) - } - } - private var thatBarfsCrapyPayload: RpcClient = object : RpcClient { - override fun invokeMethod(methodUri: UUri, requestPayload: UPayload, options: CallOptions): Flow { - return flowOf(uMessage { - payload = uPayload { - format = UPayloadFormat.UPAYLOAD_FORMAT_RAW - value = byteArrayOf(0).toByteString() - } - }) - } - } - private var thatFlowWithAnException: RpcClient = object : RpcClient { - override fun invokeMethod(methodUri: UUri, requestPayload: UPayload, options: CallOptions): Flow { - return flow { - throw RuntimeException("Boom") - } - } - } - private var thatReturnsTheWrongProto: RpcClient = object : RpcClient { - override fun invokeMethod(methodUri: UUri, requestPayload: UPayload, options: CallOptions): Flow { - val any: Any = Any.pack(Int32Value.of(42)) - return flowOf(uMessage { - payload = uPayload { - format = UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF - value = any.toByteString() - } - }) - } - } - - private var thatReturnsUMessageWithoutPayload: RpcClient = object : RpcClient { - override fun invokeMethod(methodUri: UUri, requestPayload: UPayload, options: CallOptions): Flow { - return flowOf(uMessage { - }) - } - } - - @Test - fun test_compose_happy_path() = runTest { - val payload: UPayload = buildUPayload() - returnsNumber3.invokeMethod(buildTopic(), payload, buildUCallOptions()).toResponse().map { - Int32Value.of(it.value + 5) - }.first().run { - assertEquals(Int32Value.of(8), this) - } - } - - @Test - fun test_compose_that_returns_status() = runTest { - val payload: UPayload = buildUPayload() - try { - withStatusCodeInsteadOfHappyPath.invokeMethod(buildTopic(), payload, buildUCallOptions()) - .toResponse().map { - Int32Value.of(it.value + 5) - }.first() - fail("should not reach here") - } catch (e: Exception) { - assertThrows(RuntimeException::class.java) { - throw e - } - } - } - - @Test - fun test_success_invoke_method_happy_flow_using_toResponse() = runTest { - val payload: UPayload = buildUPayload() - happyPath.invokeMethod(buildTopic(), payload, buildUCallOptions()).toResponse().first().run { - assertEquals(buildCloudEvent(), this) - } - } - - @Test - fun test_fail_invoke_method_when_invoke_method_returns_a_status_using_toResponse() = runTest { - val payload: UPayload = buildUPayload() - try { - withStatusCodeInsteadOfHappyPath.invokeMethod(buildTopic(), payload, buildUCallOptions()) - .toResponse().first() - fail("should not reach here") - } catch (e: Exception) { - assertThrows(RuntimeException::class.java) { - throw e - } - assertEquals( - "Unknown payload type [type.googleapis.com/uprotocol.v1.UStatus]. Expected " + "[io.cloudevents.v1.proto.CloudEvent]", - e.message, - ) - } - } - - @Test - fun test_fail_invoke_method_when_invoke_method_threw_an_exception_using_toResponse() = runTest { - val payload: UPayload = buildUPayload() - try { - thatFlowWithAnException.invokeMethod(buildTopic(), payload, buildUCallOptions()) - .toResponse().first() - fail("should not reach here") - } catch (e: Exception) { - assertThrows(CompletionException::class.java) { - throw e - } - assertEquals( - "Boom", - e.message, - ) - } - } - - @Test - fun test_fail_invoke_method_when_invoke_method_returns_a_bad_proto_using_toResponse() = runTest { - val payload: UPayload = buildUPayload() - try { - thatReturnsTheWrongProto.invokeMethod(buildTopic(), payload, buildUCallOptions()).toResponse() - .first() - fail("should not reach here") - } catch (e: Exception) { - assertThrows(RuntimeException::class.java) { - throw e - } - assertEquals( - "Unknown payload type [type.googleapis.com/google.protobuf.Int32Value]. Expected [io.cloudevents.v1.proto.CloudEvent]", - e.message, - ) - } - } - - @Test - @DisplayName("Invoke method that expects a UStatus payload and returns successfully with OK UStatus in the payload") - fun test_success_invoke_method_happy_flow_that_returns_status_using_toResponse() = runTest { - val payload: UPayload = buildUPayload() - withStatusCodeHappyPath.invokeMethod(buildTopic(), payload, buildUCallOptions()).toResponse().first() - .run { - assertEquals(UCode.OK, code) - assertEquals("all good", message) - } - } - - @Test - @DisplayName("test invalid payload that is not of type any") - fun test_invalid_payload_that_is_not_type_any() = runTest { - val payload: UPayload = buildUPayload() - try { - thatBarfsCrapyPayload.invokeMethod(buildTopic(), payload, buildUCallOptions()).toResponse() - .first() - fail("should not reach here") - } catch (e: Exception) { - assertThrows(RuntimeException::class.java) { - throw e - } - assertEquals( - "Protocol message contained an invalid tag (zero). [org.eclipse.uprotocol.v1.UStatus]", - e.message, - ) - } - } - - @Test - fun test_no_payload_in_umessage() = runTest { - val payload: UPayload = buildUPayload() - try { - thatReturnsUMessageWithoutPayload.invokeMethod(buildTopic(), payload, buildUCallOptions()) - .toResponse() - .first() - fail("should not reach here") - } catch (e: Exception) { - assertThrows(RuntimeException::class.java) { - throw e - } - assertEquals( - "Server returned a null payload. Expected [io.cloudevents.v1.proto.CloudEvent]", - e.message, - ) - } - } - - - @Test - fun test_toResult_nonUStatus_happy_path() = runTest { - val payload = buildUPayload() - returnsNumber3.invokeMethod(buildTopic(), payload, buildUCallOptions()).toResult().first().run { - assertTrue(isSuccess) - assertEquals(Int32Value.of(3), getOrNull()) - } - } - - @Test - fun test_toResult_uStatus_happy_path() = runTest { - val payload = buildUPayload() - withStatusCodeHappyPath.invokeMethod(buildTopic(), payload, buildUCallOptions()).toResult().first() - .run { - assertTrue(isSuccess) - assertEquals(UCode.OK, getOrNull()?.code) - assertEquals("all good", getOrNull()?.message) - - } - } - - @Test - fun test_toResult_uStatus_not_ok() = runTest { - val payload = buildUPayload() - withStatusCodeInsteadOfHappyPath.invokeMethod(buildTopic(), payload, buildUCallOptions()).toResult() - .first().run { - assertTrue(isFailure) - assertTrue(exceptionOrNull() is IllegalStateException) - assertEquals("boom, UStatus: INVALID_ARGUMENT", exceptionOrNull()?.message) - } - } - - @Test - fun test_toResult_no_payload_in_umessage() = runTest { - val payload: UPayload = buildUPayload() - thatReturnsUMessageWithoutPayload.invokeMethod(buildTopic(), payload, buildUCallOptions()) - .toResult() - .first().run { - assertTrue(isFailure) - assertTrue(exceptionOrNull() is RuntimeException) - assertEquals( - "Server returned a null payload. Expected [io.cloudevents.v1.proto.CloudEvent]", - exceptionOrNull()?.message - ) - } - } - - - @Test - fun test_fail_invoke_method_when_invoke_method_threw_an_exception_using_toResult() = runTest { - val payload: UPayload = buildUPayload() - thatFlowWithAnException.invokeMethod(buildTopic(), payload, buildUCallOptions()) - .toResult().first().run { - assertTrue(isFailure) - assertTrue(exceptionOrNull() is CompletionException) - assertEquals( - "Boom", - exceptionOrNull()?.message - ) - } - } - - @Test - fun test_fail_invoke_method_when_invoke_method_returns_a_status_using_toResult() = runTest { - val payload: UPayload = buildUPayload() - withStatusCodeInsteadOfHappyPath.invokeMethod(buildTopic(), payload, buildUCallOptions()) - .toResult().first().run { - assertTrue(isFailure) - assertTrue(exceptionOrNull() is RuntimeException) - assertEquals( - "Unknown payload type [type.googleapis.com/uprotocol.v1.UStatus]. Expected [io.cloudevents.v1.proto.CloudEvent]", - exceptionOrNull()?.message - ) - } - } - - @Test - @DisplayName("test toResult with invalid payload that is not of type any") - fun test_invalid_payload_that_is_not_type_any_toResult() = runTest { - val payload: UPayload = buildUPayload() - thatBarfsCrapyPayload.invokeMethod(buildTopic(), payload, buildUCallOptions()) - .toResult().first().run { - assertTrue(isFailure) - assertTrue(exceptionOrNull() is RuntimeException) - assertEquals( - "Protocol message contained an invalid tag (zero). [org.eclipse.uprotocol.v1.UStatus]", - exceptionOrNull()?.message - ) - } - - } - - private fun buildCloudEvent(): CloudEvent { - return CloudEvent.newBuilder().setSpecVersion("1.0").setId("HARTLEY IS THE BEST") - .setSource("https://example.com").build() - } - - private fun buildUPayload(): UPayload { - val any: Any = Any.pack(buildCloudEvent()) - return uPayload { - format = UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF - value = any.toByteString() - } - } - - private val testUMessage = uMessage { - payload = buildUPayload() - } - - private fun buildTopic(): UUri { - return LongUriSerializer.INSTANCE.deserialize("//vcu.vin/hartley/1/rpc.Raise") - } - - private fun buildUCallOptions(): CallOptions { - return callOptions { } - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/v1/UAttributesKtExtTest.kt b/src/test/kotlin/org/eclipse/uprotocol/transport/UAttributesKtExtTest.kt similarity index 74% rename from src/test/kotlin/org/eclipse/uprotocol/v1/UAttributesKtExtTest.kt rename to src/test/kotlin/org/eclipse/uprotocol/transport/UAttributesKtExtTest.kt index f3c52a1..20533cb 100644 --- a/src/test/kotlin/org/eclipse/uprotocol/v1/UAttributesKtExtTest.kt +++ b/src/test/kotlin/org/eclipse/uprotocol/transport/UAttributesKtExtTest.kt @@ -1,25 +1,18 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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.uprotocol.v1 +package org.eclipse.uprotocol.transport +import org.eclipse.uprotocol.v1.* import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Test @@ -113,12 +106,6 @@ class UAttributesKtExtTest { } private val testSink = uUri { - authority = uAuthority { name = "vcu.someVin.veh.ultifi.gm.com" } - entity = uEntity { - name = "petapp.ultifi.gm.com" - versionMajor = 1 - } - resource = uResource { forRpcResponse() } } @@ -130,10 +117,5 @@ class UAttributesKtExtTest { } private val testSource: UUri = uUri { - entity = uEntity { - name = "hartley_app" - versionMajor = 1 - } - resource = uResource { forRpcResponse() } } } diff --git a/src/test/kotlin/org/eclipse/uprotocol/transport/UMessageKtExtKtTest.kt b/src/test/kotlin/org/eclipse/uprotocol/transport/UMessageKtExtKtTest.kt new file mode 100644 index 0000000..5278749 --- /dev/null +++ b/src/test/kotlin/org/eclipse/uprotocol/transport/UMessageKtExtKtTest.kt @@ -0,0 +1,286 @@ +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.uprotocol.transport + +import com.google.protobuf.Any +import org.eclipse.uprotocol.v1.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class UMessageKtExtKtTest { + private val testSource = uUri { + ueId = 2 + ueVersionMajor = 1 + resourceId = 0 + } + private val testSink = uUri { + authorityName = "vcu.someVin.veh.ultifi.gm.com" + ueId = 1 + ueVersionMajor = 1 + resourceId = 0 + } + + private val testUUID = uUID { + val uuidJava = java.util.UUID.randomUUID() + msb = uuidJava.mostSignificantBits + lsb = uuidJava.leastSignificantBits + } + + @Test + fun testPublish() { + val publish: UMessage = uMessage { + forPublication(testSource) + } + + assertNotNull(publish) + assertEquals(UMessageType.UMESSAGE_TYPE_PUBLISH, publish.attributes.type) + assertEquals(UPriority.UPRIORITY_CS1, publish.attributes.priority) + } + + @Test + fun testNotification() { + val notification: UMessage = uMessage { + forNotification(testSource, testSink) + } + assertNotNull(notification) + assertEquals(UMessageType.UMESSAGE_TYPE_NOTIFICATION, notification.attributes.type) + assertEquals(UPriority.UPRIORITY_CS1, notification.attributes.priority) + assertEquals(testSink, notification.attributes.sink) + } + + @Test + fun testRequest() { + val ttl = 1000 + val request: UMessage = uMessage { + forRequest(testSource, testSink, ttl) + } + assertNotNull(request) + assertEquals(UMessageType.UMESSAGE_TYPE_REQUEST, request.attributes.type) + assertEquals(UPriority.UPRIORITY_CS4, request.attributes.priority) + assertEquals(testSink, request.attributes.sink) + assertEquals(ttl, request.attributes.ttl) + } + + @Test + fun testResponse() { + val reqId = testUUID + val response: UMessage = uMessage { + forResponse(testSource, testSink, reqId) + } + assertNotNull(response) + assertEquals(UMessageType.UMESSAGE_TYPE_RESPONSE, response.attributes.type) + assertEquals(UPriority.UPRIORITY_CS4, response.attributes.priority) + assertEquals(testSink, response.attributes.sink) + assertEquals(reqId, response.attributes.reqid) + } + + @Test + @DisplayName("Test response with existing request") + fun testResponseWithExistingRequest() { + val request: UMessage = uMessage { + forRequest(testSource, testSink, 1000) + + } + val response: UMessage = uMessage { + forResponse(request.attributes) + } + assertNotNull(response) + assertEquals(UMessageType.UMESSAGE_TYPE_RESPONSE, response.attributes.type) + assertEquals(UPriority.UPRIORITY_CS4, request.attributes.priority) + assertEquals(UPriority.UPRIORITY_CS4, response.attributes.priority) + assertEquals(request.attributes.source, response.attributes.sink) + assertEquals(request.attributes.sink, response.attributes.source) + assertEquals(request.attributes.id, response.attributes.reqid) + } + + @Test + @DisplayName("Test building UMessage with google.protobuf.Message payload") + fun testBuildWithPayload() { + val message: UMessage = uMessage { + forPublication(testSource) + setPayload(testSink) + } + assertNotNull(message) + assertNotNull(message.payload) + assertEquals(UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, message.attributes.payloadFormat) + assertEquals(testSink.toByteString(), message.payload) + } + + @Test + @DisplayName("Test building UMessage with UPayload payload") + fun testBuildWithUPayload() { + val message: UMessage = uMessage { + forPublication(testSource) + setPayload(UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, testSink.toByteString()) + } + assertNotNull(message) + assertNotNull(message.payload) + assertEquals(UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, message.attributes.payloadFormat) + assertEquals(testSink.toByteString(), message.payload) + } + + @Test + @DisplayName("Test building UMessage with google.protobuf.Any payload") + fun testBuildWithAnyPayload() { + val message: UMessage = uMessage { + forPublication(testSource) + setPayload(Any.getDefaultInstance()) + } + assertNotNull(message) + assertNotNull(message.payload) + assertEquals( + UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY, + message.attributes.payloadFormat + ) + assertEquals(Any.getDefaultInstance().toByteString(), message.payload) + } + + + @Test + @DisplayName("Test building response message with the wrong priority value of UPRIORITY_CS3") + fun testBuildResponseWithWrongPriority() { + val reqId = testUUID + val response: UMessage = uMessage { + forResponse(testSource, testSink, reqId) + setPriority(UPriority.UPRIORITY_CS3) + } + assertNotNull(response) + assertEquals(UMessageType.UMESSAGE_TYPE_RESPONSE, response.attributes.type) + assertEquals(UPriority.UPRIORITY_CS4, response.attributes.priority) + assertEquals(testSink, response.attributes.sink) + assertEquals(reqId, response.attributes.reqid) + } + + @Test + @DisplayName("Test building request message with the wrong priority value of UPRIORITY_CS3") + fun testBuildRequestWithWrongPriority() { + val ttl = 1000 + val request: UMessage = uMessage { + forRequest(testSource, testSink, ttl) + setPriority(UPriority.UPRIORITY_CS3) + } + + assertNotNull(request) + assertEquals(UMessageType.UMESSAGE_TYPE_REQUEST, request.attributes.type) + assertEquals(UPriority.UPRIORITY_CS4, request.attributes.priority) + assertEquals(testSink, request.attributes.sink) + assertEquals(ttl, request.attributes.ttl) + } + + @Test + @DisplayName("Test building notification message with the wrong priority value of UPRIORITY_CS0") + fun testBuildNotificationWithWrongPriority() { + val notification: UMessage = uMessage { + forNotification(testSource, testSink) + setPriority(UPriority.UPRIORITY_CS0) + } + assertNotNull(notification) + assertEquals(UMessageType.UMESSAGE_TYPE_NOTIFICATION, notification.attributes.type) + assertEquals(UPriority.UPRIORITY_CS1, notification.attributes.priority) + assertEquals(testSink, notification.attributes.sink) + } + + @Test + @DisplayName("Test building publish message with the wrong priority value of UPRIORITY_CS0") + fun testBuildPublishWithWrongPriority() { + val publish: UMessage = uMessage { + forPublication(testSource) + setPriority(UPriority.UPRIORITY_CS0) + } + assertNotNull(publish) + assertEquals(UMessageType.UMESSAGE_TYPE_PUBLISH, publish.attributes.type) + assertEquals(UPriority.UPRIORITY_CS1, publish.attributes.priority) + } + + @Test + @DisplayName("Test building publish message with the priority value of UPRIORITY_CS4") + fun testBuildPublishWithPriority() { + val publish: UMessage = uMessage { + forPublication(testSource) + setPriority(UPriority.UPRIORITY_CS4) + + } + assertNotNull(publish) + assertEquals(UMessageType.UMESSAGE_TYPE_PUBLISH, publish.attributes.type) + assertEquals(UPriority.UPRIORITY_CS4, publish.attributes.priority) + } + + @Test + @DisplayName("Test building publish message with a different Permission level") + fun testSetPermissionLevel() { + val message: UMessage = uMessage { + forPublication(testSource) + setPermissionLevel(5) + } + assertNotNull(message) + assertEquals(5, message.attributes.permissionLevel) + } + + @Test + @DisplayName("Test building publish message with a different ttl") + fun testSetTtl() { + val message: UMessage = uMessage { + forPublication(testSource) + setTtl(1000) + } + assertNotNull(message) + assertEquals(1000, message.attributes.ttl) + } + + @Test + @DisplayName("Test building publish message with a different commstatus") + fun testSetCommStatus() { + val message: UMessage = uMessage { + forPublication(testSource) + setCommStatus(UCode.OK) + } + assertNotNull(message) + assertEquals(UCode.OK, message.attributes.commstatus) + } + + @Test + @DisplayName("Test building publish message with a different reqid") + fun testSetReqid() { + val message: UMessage = uMessage { + forPublication(testSource) + setReqid(testUUID) + } + assertNotNull(message) + assertEquals(testUUID, message.attributes.reqid) + } + + @Test + @DisplayName("Test building publish message with a different token") + fun testSetToken() { + val message: UMessage = uMessage { + forPublication(testSource) + setToken("test_token") + } + assertNotNull(message) + assertEquals("test_token", message.attributes.token) + } + + @Test + @DisplayName("Test building publish message with a different traceparent") + fun testSetTraceparent() { + val message: UMessage = uMessage { + forPublication(testSource) + setTraceparent("test_traceparent") + } + assertNotNull(message) + assertEquals("test_traceparent", message.attributes.traceparent) + } +} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/transport/UPayloadExtTest.kt b/src/test/kotlin/org/eclipse/uprotocol/transport/UPayloadExtTest.kt deleted file mode 100644 index 69eaf0c..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/transport/UPayloadExtTest.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package org.eclipse.uprotocol.transport - -import org.junit.jupiter.api.Test - -import org.eclipse.uprotocol.v1.* -import kotlin.test.assertEquals -import kotlin.test.assertNull - -class UPayloadExtTest { - - @Test - fun test_unpack_with_UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY(){ - val message = uStatus { } - val payload = uPayload { - packToAny(message) - } - val result = unpack(payload, UStatus::class.java) - checkNotNull(result) - assertEquals(message, result) - } - - @Test - fun test_unpack_with_UPAYLOAD_FORMAT_PROTOBUF(){ - val message = uStatus { } - val payload = uPayload { - pack(message) - } - val result = unpack(payload, UStatus::class.java) - checkNotNull(result) - assertEquals(message, result) - } - - @Test - fun test_unpack_with_UPAYLOAD_FORMAT_RAW(){ - val message = uStatus { } - val payload = uPayload { - format = UPayloadFormat.UPAYLOAD_FORMAT_RAW - value = message.toByteString() - } - val result = unpack(payload, UStatus::class.java) - assertNull(result) - } - - @Test - fun test_unpack_but_InvalidProtocolBufferException(){ - val message = uStatus { } - val payload = uPayload { - packToAny(message) - } - val result = unpack(payload, UUri::class.java) - assertNull(result) - } - - @Test - fun test_inline_unpack_with_UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY(){ - val message = uStatus { } - val payload = uPayload { - packToAny(message) - } - val result = payload.unpack() - checkNotNull(result) - assertEquals(message, result) - } - - @Test - fun test_inline_unpack_with_UPAYLOAD_FORMAT_PROTOBUF(){ - val message = uStatus { } - val payload = uPayload { - pack(message) - } - val result = payload.unpack() - checkNotNull(result) - assertEquals(message, result) - } - - @Test - fun test_inline_unpack_with_UPAYLOAD_FORMAT_RAW(){ - val message = uStatus { } - val payload = uPayload { - format = UPayloadFormat.UPAYLOAD_FORMAT_RAW - value = message.toByteString() - } - val result = payload.unpack() - assertNull(result) - } - - @Test - fun test_inline_unpack_but_InvalidProtocolBufferException(){ - val message = uStatus { } - val payload = uPayload { - packToAny(message) - } - val result = payload.unpack() - assertNull(result) - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/transport/UTransportTest.kt b/src/test/kotlin/org/eclipse/uprotocol/transport/UTransportTest.kt index 8606d65..e1aedcf 100644 --- a/src/test/kotlin/org/eclipse/uprotocol/transport/UTransportTest.kt +++ b/src/test/kotlin/org/eclipse/uprotocol/transport/UTransportTest.kt @@ -1,22 +1,14 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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.uprotocol.transport @@ -31,19 +23,18 @@ import org.junit.jupiter.api.Test * Test implementing and using uTransport API */ class UTransportTest { - @Test - @DisplayName("Test happy path send message parts") - fun test_happy_send_message_parts() { - val transport: UTransport = HappyUTransport() - val status = transport.send(UMessage.getDefaultInstance()) - assertEquals(status.code, UCode.OK) - } - @Test @DisplayName("Test happy path send message") fun test_happy_send_message() { val transport: UTransport = HappyUTransport() - val status = transport.send(UMessage.getDefaultInstance()) + val uri = uUri { + ueId = 1 + ueVersionMajor = 1 + resourceId = 0x8000 + + } + + val status = transport.send(uMessage { forPublication(uri) }) assertEquals(status.code, UCode.OK) } @@ -51,7 +42,7 @@ class UTransportTest { @DisplayName("Test happy path register listener") fun test_happy_register_listener() { val transport: UTransport = HappyUTransport() - val status = transport.registerListener(UUri.getDefaultInstance(), MyListener()) + val status = transport.registerListener(UUri.getDefaultInstance(), listener = MyListener()) assertEquals(status.code, UCode.OK) } @@ -59,24 +50,15 @@ class UTransportTest { @DisplayName("Test happy path unregister listener") fun test_happy_register_unlistener() { val transport: UTransport = HappyUTransport() - val status = transport.unregisterListener(UUri.getDefaultInstance(), MyListener()) + val status = transport.unregisterListener(UUri.getDefaultInstance(), listener = MyListener()) assertEquals(status.code, UCode.OK) } - @Test - @DisplayName("Test unhappy path send message parts") - fun test_unhappy_send_message_parts() { - val transport: UTransport = SadUTransport() - val status = transport.send(UMessage.getDefaultInstance()) - - assertEquals(status.code, UCode.INTERNAL) - } - @Test @DisplayName("Test unhappy path send message") fun test_unhappy_send_message() { val transport: UTransport = SadUTransport() - val status = transport.send(UMessage.getDefaultInstance()) + val status = transport.send(uMessage { }) assertEquals(status.code, UCode.INTERNAL) } @@ -84,7 +66,7 @@ class UTransportTest { @DisplayName("Test unhappy path register listener") fun test_unhappy_register_listener() { val transport: UTransport = SadUTransport() - val status = transport.registerListener(UUri.getDefaultInstance(), MyListener()) + val status = transport.registerListener(UUri.getDefaultInstance(), listener = MyListener()) assertEquals(status.code, UCode.INTERNAL) } @@ -92,10 +74,26 @@ class UTransportTest { @DisplayName("Test unhappy path unregister listener") fun test_unhappy_register_unlistener() { val transport: UTransport = SadUTransport() - val status = transport.unregisterListener(UUri.getDefaultInstance(), MyListener()) + val status = transport.unregisterListener(UUri.getDefaultInstance(), listener = MyListener()) assertEquals(status.code, UCode.INTERNAL) } + @Test + @DisplayName("Test happy path registerlistener with source filter only") + fun test_happy_register_listener_source_filter() { + val transport: UTransport = HappyUTransport() + val status = transport.registerListener(UUri.getDefaultInstance(), listener = MyListener()) + assertEquals(status.code, UCode.OK) + } + + @Test + @DisplayName("Test happy path unregisterlistener with source filter only") + fun test_happy_unregister_listener_source_filter() { + val transport: UTransport = HappyUTransport() + val status = transport.unregisterListener(UUri.getDefaultInstance(), listener = MyListener()) + assertEquals(status.code, UCode.OK) + } + internal inner class MyListener : UListener { override fun onReceive(message: UMessage) {} } @@ -107,18 +105,22 @@ class UTransportTest { } } - override fun registerListener(topic: UUri, listener: UListener): UStatus { + override fun registerListener(sourceFilter: UUri, sinkFilter: UUri?, listener: UListener): UStatus { listener.onReceive(uMessage { }) return uStatus { code = UCode.OK } } - override fun unregisterListener(topic: UUri, listener: UListener): UStatus { + override fun unregisterListener(sourceFilter: UUri, sinkFilter: UUri?, listener: UListener): UStatus { return uStatus { code = UCode.OK } } + + override fun getSource(): UUri { + return uUri { } + } } private inner class SadUTransport : UTransport { @@ -128,17 +130,23 @@ class UTransportTest { } } - override fun registerListener(topic: UUri, listener: UListener): UStatus { + override fun registerListener(sourceFilter: UUri, sinkFilter: UUri?, listener: UListener): UStatus { listener.onReceive(uMessage { }) return uStatus { code = UCode.INTERNAL } } - override fun unregisterListener(topic: UUri, listener: UListener): UStatus { + override fun unregisterListener(sourceFilter: UUri, sinkFilter: UUri?, listener: UListener): UStatus { return uStatus { code = UCode.INTERNAL } } + + override fun getSource(): UUri { + return uUri { } + } } + + } \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/transport/validator/UAttributesValidatorTest.kt b/src/test/kotlin/org/eclipse/uprotocol/transport/validator/UAttributesValidatorTest.kt index 32db1ac..f3974ab 100644 --- a/src/test/kotlin/org/eclipse/uprotocol/transport/validator/UAttributesValidatorTest.kt +++ b/src/test/kotlin/org/eclipse/uprotocol/transport/validator/UAttributesValidatorTest.kt @@ -1,29 +1,20 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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.uprotocol.transport.validator -import org.eclipse.uprotocol.transport.validate.* -import org.eclipse.uprotocol.transport.validate.UAttributesValidator.Companion.getValidator -import org.eclipse.uprotocol.uri.serializer.LongUriSerializer -import org.eclipse.uprotocol.uuid.factory.UUIDV8 +import org.eclipse.uprotocol.transport.* +import org.eclipse.uprotocol.transport.validator.UAttributesValidator.Companion.getValidator import org.eclipse.uprotocol.v1.* import org.eclipse.uprotocol.validation.ValidationResult import org.junit.jupiter.api.Assertions.* @@ -32,830 +23,537 @@ import org.junit.jupiter.api.Test internal class UAttributesValidatorTest { - @Test - @DisplayName("test fetching validator for valid types") - fun test_fetching_validator_for_valid_types() { - val publish: UAttributesValidator = uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - }.getValidator() - - assertEquals("UAttributesValidator.Publish", publish.toString()) - val request: UAttributesValidator = uAttributes { - forRequest(testSource, testSink, UPriority.UPRIORITY_CS4, 1000) - }.getValidator() - - assertEquals("UAttributesValidator.Request", request.toString()) - val response: UAttributesValidator = uAttributes { - forResponse(testSource, testSink, UPriority.UPRIORITY_CS4, UUIDV8()) - }.getValidator() - - assertEquals("UAttributesValidator.Response", response.toString()) - - val notification: UAttributesValidator = uAttributes { - forNotification(testSource, testSink, UPriority.UPRIORITY_CS4) - }.getValidator() - - assertEquals("UAttributesValidator.Notification", notification.toString()) + private val defaultUUri = uUri { + ueId=1 + ueVersionMajor=1 + resourceId=0 } - @Test - @DisplayName("test using notification validator for publish type message") - fun test_using_notification_validator_for_publish_type_message() { - val attributes: UAttributes = uAttributes { forPublication(testSource, UPriority.UPRIORITY_CS0) } - val validator: UAttributesValidator = Notification - val status = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Wrong Attribute Type [UMESSAGE_TYPE_PUBLISH],Missing Sink", status.getMessage()) + private val methodUUri = uUri { + ueId=1 + ueVersionMajor=1 + resourceId=1 } - @Test - @DisplayName("Validate a UAttributes for payload that is meant to be published") - fun test_validate_uAttributes_for_publish_message_payload() { - val attributes: UAttributes = uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isSuccess()) - assertEquals("", status.getMessage()) + private val topicUUri = uUri { + ueId=1 + ueVersionMajor=1 + resourceId=0x8000 } - @Test - @DisplayName("Validate a UAttributes for payload that is meant to be published with all values") - fun test_validate_uAttributes_for_publish_message_payload_all_values() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - ttl = 1000 - sink = testSink - permissionLevel = 2 - commstatus = UCode.INVALID_ARGUMENT - reqid = UUIDV8() - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validate(attributes) + @DisplayName("Test creating a UMessage of type publish then validating it using UAttributeValidator for the happy path") + fun testUAttributeValidatorHappyPath() { + val message = uMessage { + forPublication(topicUUri) + } + val validator = message.attributes.getValidator() + val status: ValidationResult = validator.validate(message.attributes) assertTrue(status.isSuccess()) - assertEquals("", status.getMessage()) + assertEquals(validator.toString(), "UAttributesValidator.Publish") } @Test - @DisplayName("Validate a UAttributes for payload that is meant to be published with invalid type") - fun test_validate_uAttributes_for_publish_message_payload_invalid_type() { - val attributes: UAttributes = uAttributes { - forResponse( - testSource, testSink, - UPriority.UPRIORITY_CS0, UUIDV8() - ) + @DisplayName("Test validation a notification message using UAttributeValidator") + fun testUAttributeValidatorNotification() { + val message: UMessage = uMessage { + forNotification(topicUUri, defaultUUri) } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Wrong Attribute Type [UMESSAGE_TYPE_RESPONSE]", status.getMessage()) + val validator: UAttributesValidator = message.attributes.getValidator() + val result = validator.validate(message.attributes) + assertTrue(result.isSuccess()) + assertEquals(validator.toString(), "UAttributesValidator.Notification") } @Test - @DisplayName("Validate a UAttributes for payload that is meant to be published with invalid time to live") - fun test_validate_uAttributes_for_publish_message_payload_invalid_ttl() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - ttl = -1 - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid TTL [-1]", status.getMessage()) + @DisplayName("Test validation a request message using UAttributeValidator") + fun testUAttributeValidatorRequest() { + val message: UMessage = uMessage { + forRequest(defaultUUri, methodUUri, 1000) + } + val validator: UAttributesValidator = message.attributes.getValidator() + val result = validator.validate(message.attributes) + assertTrue(result.isSuccess()) + assertEquals(validator.toString(), "UAttributesValidator.Request") } @Test - @DisplayName("Validate a UAttributes for payload that is meant to be published with invalid sink") - fun test_validate_uAttributes_for_publish_message_payload_invalid_sink() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - sink = UUri.getDefaultInstance() - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Uri is empty.", status.getMessage()) - } + @DisplayName("Test validation a response message using UAttributeValidator") + fun testUAttributeValidatorResponse() { + val request: UMessage =uMessage { + forRequest(defaultUUri, methodUUri, 1000) + } + val response: UMessage = uMessage { + forResponse(methodUUri, defaultUUri, request.attributes.id) + } - @Test - @DisplayName("Validate a UAttributes for payload that is meant to be published with invalid permission level") - fun test_validate_uAttributes_for_publish_message_payload_invalid_permission_level() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - permissionLevel = -42 - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid Permission Level", status.getMessage()) - } - - @Test - @DisplayName("Validate a UAttributes for payload that is meant to be published with invalid request id") - fun test_validate_uAttributes_for_publish_message_payload_invalid_request_id() { - val uuidJava: java.util.UUID = java.util.UUID.randomUUID() - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - reqid = uUID { - msb = uuidJava.mostSignificantBits - lsb = uuidJava.leastSignificantBits - } - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid UUID", status.getMessage()) + val validator: UAttributesValidator = response.attributes.getValidator() + val result = validator.validate(response.attributes) + assertTrue(result.isSuccess()) + assertEquals(validator.toString(), "UAttributesValidator.Response") + assertEquals(result.getMessage(), "") } - // ---- @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC request") - fun test_validate_uAttributes_for_rpc_request_message_payload() { - val attributes: UAttributes = - uAttributes { - forRequest(testSource, testSink, UPriority.UPRIORITY_CS4, 1000) - } + @DisplayName("Test validation a response message using UAttributeValidator when passed request UAttributes") + fun testUAttributeValidatorResponseWithRequestAttributes() { + val request: UMessage =uMessage { + forRequest(defaultUUri, methodUUri, 1000) + } + val response: UMessage = uMessage { + forResponse(request.attributes) + } - val validator: UAttributesValidator = Request - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isSuccess()) - assertEquals("", status.getMessage()) + val validator: UAttributesValidator = response.attributes.getValidator() + val result = validator.validate(response.attributes) + assertTrue(result.isSuccess()) + assertEquals(validator.toString(), "UAttributesValidator.Response") } @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC request with all values") - fun test_validate_uAttributes_for_rpc_request_message_payload_all_values() { - val attributes: UAttributes = - uAttributes { - forRequest(testSource, testSink, UPriority.UPRIORITY_CS4, 1000) - permissionLevel = 2 - commstatus = UCode.INVALID_ARGUMENT - reqid = UUIDV8() - - } + @DisplayName("Test validation failed when using the publish validator to test request messages") + fun testUAttributeValidatorRequestWithPublishValidator() { + val message: UMessage =uMessage { + forRequest(defaultUUri, methodUUri, 1000) + } - val validator: UAttributesValidator = Request - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isSuccess()) - assertEquals("", status.getMessage()) + val validator: UAttributesValidator = Publish + val result = validator.validate(message.attributes) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Publish") + assertEquals(result.getMessage(), "Wrong Attribute Type [UMESSAGE_TYPE_REQUEST],Sink should not be present") } @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC request with invalid type") - fun test_validate_uAttributes_for_rpc_request_message_payload_invalid_type() { - val attributes: UAttributes = uAttributes { - forResponse( - testSource, testSink, - UPriority.UPRIORITY_CS4, UUIDV8() - ) - ttl = 1000 + @DisplayName("Test validation failed when using the notification validator to test publish messages") + fun testUAttributeValidatorPublishWithNotificationValidator() { + val message: UMessage = uMessage { + forPublication(topicUUri) } - val validator: UAttributesValidator = Request - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Wrong Attribute Type [UMESSAGE_TYPE_RESPONSE]", status.getMessage()) - } - - @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC request with invalid time to live") - fun test_validate_uAttributes_for_rpc_request_message_payload_invalid_ttl() { - val attributes: UAttributes = - uAttributes { - forRequest(testSource, testSink, UPriority.UPRIORITY_CS4, -1) - } - val validator: UAttributesValidator = Request - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid TTL [-1]", status.getMessage()) - } - - @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC request with invalid sink") - fun test_validate_uAttributes_for_rpc_request_message_payload_invalid_sink() { - val attributes: UAttributes = - uAttributes { - forRequest(testSource, UUri.getDefaultInstance(), UPriority.UPRIORITY_CS4, 1000) - } - val validator: UAttributesValidator = Request - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Uri is empty.", status.getMessage()) + val validator: UAttributesValidator = Notification + val result = validator.validate(message.attributes) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Notification") + assertEquals(result.getMessage(), "Wrong Attribute Type [UMESSAGE_TYPE_PUBLISH],Missing Sink") } @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC request with invalid permission level") - fun test_validate_uAttributes_for_rpc_request_message_payload_invalid_permission_level() { - val attributes: UAttributes = - uAttributes { - forRequest(testSource, testSink, UPriority.UPRIORITY_CS4, 1000) - permissionLevel = -42 - } + @DisplayName("Test validation failed when using the request validator to test response messages") + fun testUAttributeValidatorResponseWithRequestValidator() { + val request: UMessage =uMessage { + forRequest(defaultUUri, methodUUri, 1000) + } + val response: UMessage = uMessage { + forResponse(request.attributes) + } val validator: UAttributesValidator = Request - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid Permission Level", status.getMessage()) + val result = validator.validate(response.attributes) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Request") + assertEquals( + result.getMessage(), + "Wrong Attribute Type [UMESSAGE_TYPE_RESPONSE],Missing TTL,Invalid Sink Uri,Message should not have a reqid" + ) } @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC request with invalid request id") - fun test_validate_uAttributes_for_rpc_request_message_payload_invalid_request_id() { - val uuidJava: java.util.UUID = java.util.UUID.randomUUID() - val attributes: UAttributes = - uAttributes { - forRequest(testSource, testSink, UPriority.UPRIORITY_CS4, 1000) - reqid = uUID { - msb = uuidJava.mostSignificantBits - lsb = uuidJava.leastSignificantBits - } - } + @DisplayName("Test validation failed when using the response validator to test notification messages") + fun testUAttributeValidatorNotificationWithResponseValidator() { + val message: UMessage = uMessage { + forNotification(topicUUri, defaultUUri) + } - val validator: UAttributesValidator = Request - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid UUID", status.getMessage()) + val validator: UAttributesValidator = Response + val result = validator.validate(message.attributes) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Response") + assertEquals( + result.getMessage(), + "Wrong Attribute Type [UMESSAGE_TYPE_NOTIFICATION],Invalid UPriority [UPRIORITY_CS1],Missing correlationId" + ) } - // ---- @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC response") - fun test_validate_uAttributes_for_rpc_response_message_payload() { - val attributes: UAttributes = uAttributes { - forResponse( - testSource, testSink, - UPriority.UPRIORITY_CS4, UUIDV8() - ) + @DisplayName("Test validation of request message has an invalid sink attribute") + fun testUAttributeValidatorRequestMissingSink() { + val message: UMessage = uMessage { + forRequest(defaultUUri, defaultUUri, 1000) } - val validator: UAttributesValidator = Response - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isSuccess()) - assertEquals("", status.getMessage()) + + val validator: UAttributesValidator = message.attributes.getValidator() + val result = validator.validate(message.attributes) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Request") + assertEquals(result.getMessage(), "Invalid Sink Uri") } @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC response with all values") - fun test_validate_uAttributes_for_rpc_response_message_payload_all_values() { - val attributes: UAttributes = uAttributes { - forResponse( - testSource, testSink, - UPriority.UPRIORITY_CS4, UUIDV8() - ) - permissionLevel = 2 - commstatus = UCode.INVALID_ARGUMENT - + @DisplayName("Test validation of request message that has a permission level that is less than 0") + fun testUAttributeValidatorRequestInvalidPermissionLevel() { + val message: UMessage = uMessage { + forRequest(defaultUUri, methodUUri, 1000) + setPermissionLevel(-1) } - val validator: UAttributesValidator = Response - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isSuccess()) - assertEquals("", status.getMessage()) - } - @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC response with invalid type") - fun test_validate_uAttributes_for_rpc_response_message_payload_invalid_type() { - val attributes: UAttributes = - uAttributes { - forNotification(testSource, testSink, UPriority.UPRIORITY_CS4) - } - val validator: UAttributesValidator = Response - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Wrong Attribute Type [UMESSAGE_TYPE_NOTIFICATION],Missing correlationId", status.getMessage()) + val validator: UAttributesValidator = message.attributes.getValidator() + val result = validator.validate(message.attributes) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Request") + assertEquals(result.getMessage(), "Invalid Permission Level") } @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC response with invalid time to live") - fun test_validate_uAttributes_for_rpc_response_message_payload_invalid_ttl() { - val attributes: UAttributes = uAttributes { - forResponse( - testSource, testSink, - UPriority.UPRIORITY_CS4, UUIDV8() - ) - ttl = -1 + @DisplayName("Test validation of request message that has a permission level that is greater than 0") + fun testUAttributeValidatorRequestValidPermissionLevel() { + val message: UMessage = uMessage { + forRequest(defaultUUri, methodUUri, 1000) + setPermissionLevel(1) } - val validator: UAttributesValidator = Response - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid TTL [-1]", status.getMessage()) - } - - @Test - @DisplayName( - "Validate a UAttributes for payload that is meant to be an RPC response with missing sink and " + "missing request id" - ) - fun test_validate_uAttributes_for_rpc_response_message_payload_missing_sink_and_missing_requestId() { - val attributes: UAttributes = - uAttributes { - forResponse( - testSource, - UUri.getDefaultInstance(), - UPriority.UPRIORITY_CS4, - UUID.getDefaultInstance() - ) - } - val validator: UAttributesValidator = Response - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Missing Sink,Missing correlationId", status.getMessage()) + + val validator: UAttributesValidator = message.attributes.getValidator() + val result = validator.validate(message.attributes) + assertTrue(result.isSuccess()) + assertFalse(validator.isExpired(message.attributes)) + assertEquals(validator.toString(), "UAttributesValidator.Request") } @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC response with invalid permission level") - fun test_validate_uAttributes_for_rpc_response_message_payload_invalid_permission_level() { - val attributes: UAttributes = uAttributes { - forResponse( - testSource, testSink, - UPriority.UPRIORITY_CS4, UUIDV8() - ) - permissionLevel = -42 + @DisplayName("Test validation of request message that has TTL that is less than 0") + fun testUAttributeValidatorRequestInvalidTTL() { + val message: UMessage = uMessage { + forPublication(topicUUri) + setTtl(-1) } - val validator: UAttributesValidator = Response - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid Permission Level", status.getMessage()) - } - @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC response with missing request id") - fun test_validate_uAttributes_for_rpc_response_message_payload_missing_request_id() { - val attributes: UAttributes = - uAttributes { - forResponse(testSource, testSink, UPriority.UPRIORITY_CS4, UUID.getDefaultInstance()) - } - val validator: UAttributesValidator = Response - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Missing correlationId", status.getMessage()) + val validator: UAttributesValidator = message.attributes.getValidator() + val result = validator.validate(message.attributes) + assertTrue(result.isFailure()) + assertFalse(validator.isExpired(message.attributes)) + assertEquals(validator.toString(), "UAttributesValidator.Publish") + assertEquals(result.getMessage(), "Invalid TTL [-1]") } @Test - @DisplayName("Validate a UAttributes for payload that is meant to be an RPC response with invalid request id") - fun test_validate_uAttributes_for_rpc_response_message_payload_invalid_request_id() { - val uuidJava: java.util.UUID = java.util.UUID.randomUUID() - val reqid: UUID = uUID { - msb = uuidJava.mostSignificantBits - lsb = uuidJava.leastSignificantBits + @DisplayName("Test validation of request message where the message has expired") +// @Throws( +// InterruptedException::class +// ) + fun testUAttributeValidatorRequestExpired() { + val message: UMessage = uMessage { + forRequest(defaultUUri, methodUUri, 1) } - val attributes: UAttributes = - uAttributes { forResponse(testSource, testSink, UPriority.UPRIORITY_CS4, reqid) } - val validator: UAttributesValidator = Response - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid correlationId [$reqid]", status.getMessage()) + Thread.sleep(100) + val validator: UAttributesValidator = message.attributes.getValidator() + assertTrue(validator.isExpired(message.attributes)) } - // ---- @Test - @DisplayName("Validate a UAttributes for payload that is meant to be published not expired") - fun test_validate_uAttributes_for_publish_message_payload_not_expired() { - val attributes: UAttributes = uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) + @DisplayName("Test validator isExpired() for an ID that is mall formed and doesn't have the time") + fun testUAttributeValidatorRequestExpiredMalformedId() { + val attributes = uAttributes { + id = uUID { } + type = UMessageType.UMESSAGE_TYPE_REQUEST } - val validator: UAttributesValidator = Publish + val validator: UAttributesValidator = attributes.getValidator() assertFalse(validator.isExpired(attributes)) } @Test - @DisplayName("Validate a UAttributes isExpired() for an invalid UUID of UAttributes") - fun test_validate_uAttributes_isExpired_for_invalid_UUID() { - val attributes = UAttributes.getDefaultInstance() - val validator = Publish - assertFalse(validator.isExpired(attributes)) - } + @DisplayName("Test validation fails when a publish messages has a reqid") + fun testUAttributeValidatorPublishWithReqId() { + val publish: UMessage = uMessage { + forPublication(topicUUri) + } - @Test - @DisplayName("Validate a UAttributes for payload that is meant to be published not expired with ttl zero") - fun test_validate_uAttributes_for_publish_message_payload_not_expired_with_ttl_zero() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - ttl = 0 - } - val validator: UAttributesValidator = Publish - assertFalse(validator.isExpired(attributes)) - } + val attributes = publish.attributes.copy { + reqid = uUID { } + } - @Test - @DisplayName("Validate a UAttributes for payload that is meant to be published not expired with ttl") - fun test_validate_uAttributes_for_publish_message_payload_not_expired_with_ttl() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - ttl = 10000 - } - val validator: UAttributesValidator = Publish - assertFalse(validator.isExpired(attributes)) + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Publish") + assertEquals(result.getMessage(), "Message should not have a reqid") } @Test - @DisplayName("Validate a UAttributes for payload that is meant to be published not expired with ttl") - fun test_validate_uAttributes_for_publish_message_payload_with_negative_ttl() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - ttl = -1 - } - - val validator: UAttributesValidator = Publish - assertFalse(validator.isExpired(attributes)) - } + @DisplayName("Test notification validation where the sink is missing") + fun testUAttributeValidatorNotificationMissingSink() { + val message: UMessage =uMessage { + forNotification(topicUUri, defaultUUri) + } + val attributes = message.attributes.copy { clearSink() } + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) - @Test - @DisplayName("Validate a UAttributes for payload that is meant to be published expired with ttl") - @Throws( - InterruptedException::class - ) - fun test_validate_uAttributes_for_publish_message_payload_expired_with_ttl() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - ttl = 1 - } - Thread.sleep(800) - val validator: UAttributesValidator = Publish - assertTrue(validator.isExpired(attributes)) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Notification") + assertEquals(result.getMessage(), "Missing Sink") } - // ---- @Test - @DisplayName("test validating publish invalid ttl attribute") - fun test_validating_publish_invalid_ttl_attribute() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - ttl = -1 - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validateTtl(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid TTL [-1]", status.getMessage()) - } + @DisplayName("Test notification validation where the sink the default instance") + fun testUAttributeValidatorNotificationDefaultSink() { + val message: UMessage =uMessage { + forNotification(topicUUri, defaultUUri) + } + val attributes = message.attributes.copy { + sink = UUri.getDefaultInstance() + } + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) - @Test - @DisplayName("test validating publish valid ttl attribute") - fun test_validating_valid_ttl_attribute() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - ttl = 100 - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validateTtl(attributes) - assertEquals(ValidationResult.success(), status) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Notification") + assertEquals(result.getMessage(), "Missing Sink") } @Test - @DisplayName("test validating invalid sink attribute") - fun test_validating_invalid_sink_attribute() { - val uri: UUri = LongUriSerializer.INSTANCE.deserialize("//") - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - sink = uri - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validateSink(attributes) - assertTrue(status.isFailure()) - assertEquals("Uri is empty.", status.getMessage()) - } + @DisplayName("Test notification validation where the sink is NOT the defaultResourceId") + fun testUAttributeValidatorNotificationDefaultResourceId() { + val message: UMessage = uMessage { + forNotification(topicUUri, topicUUri) + } + val validator: UAttributesValidator = message.attributes.getValidator() + val result = validator.validate(message.attributes) - @Test - @DisplayName("test validating valid sink attribute") - fun test_validating_valid_sink_attribute() { - val uri: UUri = LongUriSerializer.INSTANCE.deserialize("/haartley/1") - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - sink = uri - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validateSink(attributes) - assertEquals(ValidationResult.success(), status) - } - - @Test - @DisplayName("test validating invalid ReqId attribute") - fun test_validating_invalid_ReqId_attribute() { - val uuidJava: java.util.UUID = java.util.UUID.randomUUID() - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - reqid = uUID { - msb = uuidJava.mostSignificantBits - lsb = uuidJava.leastSignificantBits - } - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validateReqId(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid UUID", status.getMessage()) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Notification") + assertEquals(result.getMessage(), "Invalid Sink Uri") } @Test - @DisplayName("test validating valid ReqId attribute") - fun test_validating_valid_ReqId_attribute() { - val attributes: UAttributes = uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - reqid = UUIDV8() + @DisplayName("Test validatePriority when priority is less than CS1") + fun testUAttributeValidatorValidatePriorityLessThanCS1() { + val message: UMessage = uMessage { + forPublication(topicUUri) } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validateReqId(attributes) - assertEquals(ValidationResult.success(), status) - } + val attributes = message.attributes.copy { priority = UPriority.UPRIORITY_CS0 } + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) - @Test - @DisplayName("test validating invalid PermissionLevel attribute") - fun test_validating_invalid_PermissionLevel_attribute() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - permissionLevel = -1 - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validatePermissionLevel(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid Permission Level", status.getMessage()) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Publish") + assertEquals(result.getMessage(), "Invalid UPriority [UPRIORITY_CS0]") } @Test - @DisplayName("test validating valid PermissionLevel attribute") - fun test_validating_valid_PermissionLevel_attribute() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - permissionLevel = 3 - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validatePermissionLevel(attributes) - assertEquals(ValidationResult.success(), status) - } + @DisplayName("Test validateId when id is missing") + fun testUAttributeValidatorValidateIdMissing() { + val message: UMessage = uMessage { + forPublication(topicUUri) + } + val attributes = message.attributes.copy { clearId() } + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) - @Test - @DisplayName("test validating valid PermissionLevel attribute") - fun test_validating_valid_PermissionLevel_attribute_invalid() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - permissionLevel = 0 - } - val validator: UAttributesValidator = Publish - val status: ValidationResult = validator.validatePermissionLevel(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid Permission Level", status.getMessage()) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Publish") + assertEquals(result.getMessage(), "Missing id") } @Test - @DisplayName("test validating request message types") - fun test_validating_request_message_types() { - val attributes: UAttributes = - uAttributes { - forRequest(testSource, testSink, UPriority.UPRIORITY_CS6, 100) - } - + @DisplayName("Test validateId when id is the default instance") + fun testUAttributeValidatorValidateIdDefault() { + val message: UMessage = uMessage { + forPublication(topicUUri) + } + val attributes = message.attributes.copy { id = UUID.getDefaultInstance() } val validator: UAttributesValidator = attributes.getValidator() - assertEquals("UAttributesValidator.Request", validator.toString()) - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isSuccess()) - assertEquals("", status.getMessage()) - } + val result = validator.validate(attributes) - @Test - @DisplayName("test validating request validator using wrong messagetype") - fun test_validating_request_validator_with_wrong_messagetype() { - val attributes: UAttributes = uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS6) - } - val validator: UAttributesValidator = Request - assertEquals("UAttributesValidator.Request", validator.toString()) - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Wrong Attribute Type [UMESSAGE_TYPE_PUBLISH],Missing TTL,Missing Sink", status.getMessage()) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Publish") + assertEquals(result.getMessage(), "Attributes must contain valid uProtocol UUID in id property") } @Test - @DisplayName("test validating request validator using bad ttl") - fun test_validating_request_validator_with_wrong_bad_ttl() { - val attributes: UAttributes = uAttributes { - forRequest( - testSource, LongUriSerializer.INSTANCE.deserialize("/hartley/1/rpc.response"), - UPriority.UPRIORITY_CS6, -1 - ) + @DisplayName("Test publish validateSink when sink is not empty") + fun testUAttributeValidatorValidateSinkNotEmpty() { + val message: UMessage = uMessage { + forPublication(topicUUri) } + val attributes: UAttributes = message.attributes.copy { sink = defaultUUri } + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) - val validator: UAttributesValidator = Request - assertEquals("UAttributesValidator.Request", validator.toString()) - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid TTL [-1]", status.getMessage()) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Publish") + assertEquals(result.getMessage(), "Sink should not be present") } @Test - @DisplayName("test validating response validator using bad ttl") - fun test_validating_response_validator_with_wrong_bad_ttl() { - val attributes: UAttributes = uAttributes { - forResponse( - testSource, - LongUriSerializer.INSTANCE.deserialize("/hartley/1/rpc.response"), - UPriority.UPRIORITY_CS6, - UUIDV8() - ) - ttl = -1 + @DisplayName("Test validateSink of a request message that is missing a sink") + fun testUAttributeValidatorValidateSinkMissing() { + val message: UMessage = uMessage { + forRequest(defaultUUri, methodUUri, 1000) } - val validator: UAttributesValidator = Response - assertEquals("UAttributesValidator.Response", validator.toString()) - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Invalid TTL [-1]", status.getMessage()) - } - @Test - @DisplayName("test validating publish validator with wrong messagetype") - fun test_validating_publish_validator_with_wrong_messagetype() { - val attributes: UAttributes = - uAttributes { - forRequest(testSource, testSink, UPriority.UPRIORITY_CS6, 1000) - } + val attributes = message.attributes.copy { clearSink() } + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) - val validator: UAttributesValidator = Publish - assertEquals("UAttributesValidator.Publish", validator.toString()) - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Wrong Attribute Type [UMESSAGE_TYPE_REQUEST]", status.getMessage()) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Request") + assertEquals(result.getMessage(), "Missing Sink") } @Test - @DisplayName("test validating response validator with wrong messagetype") - fun test_validating_response_validator_with_wrong_messagetype() { - val attributes: UAttributes = uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS6) + @DisplayName("Test validateTtl of a request message where ttl is less than 0") + fun testUAttributeValidatorValidateTtlLessThanZero() { + val message: UMessage = uMessage { + forRequest(defaultUUri, methodUUri, -1) } - val validator: UAttributesValidator = Response - assertEquals("UAttributesValidator.Response", validator.toString()) - val status: ValidationResult = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals( - "Wrong Attribute Type [UMESSAGE_TYPE_PUBLISH],Missing Sink,Missing correlationId", status.getMessage() - ) - } + val validator: UAttributesValidator = message.attributes.getValidator() + val result = validator.validate(message.attributes) - @Test - @DisplayName("test validating request containing token") - fun test_validating_request_containing_token() { - val attributes: UAttributes = - uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - token = "null" - } - val validator: UAttributesValidator = attributes.getValidator() - assertEquals("UAttributesValidator.Publish", validator.toString()) - val status: ValidationResult = validator.validate(attributes) - assertEquals(ValidationResult.success(), status) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Request") + assertEquals(result.getMessage(), "Invalid TTL [-1]") } @Test - @DisplayName("test_valid_request_methoduri_in_sink") - fun test_valid_request_methoduri_in_sink() { - val testSink = LongUriSerializer.INSTANCE.deserialize("/test.service/1/rpc.method") - val attributes = uAttributes { - forRequest(testSource, testSink, UPriority.UPRIORITY_CS4, 3000) + @DisplayName("Test validatePriority of a request message where priority is less than CS4") + fun testUAttributeValidatorValidatePriorityLessThanCS4() { + val message: UMessage = uMessage { + forRequest(defaultUUri, methodUUri, 1000) + } + val attributes = message.attributes.copy { + priority = UPriority.UPRIORITY_CS3 } + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) - val validator = attributes.getValidator() - assertEquals("UAttributesValidator.Request", validator.toString()) - val status = validator.validate(attributes) - assertEquals(ValidationResult.success(), status) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Request") + assertEquals(result.getMessage(), "Invalid UPriority [UPRIORITY_CS3]") } @Test - @DisplayName("test_invalid_request_methoduri_in_sink") - fun test_invalid_request_methoduri_in_sink() { - val testSink = LongUriSerializer.INSTANCE.deserialize("/test.client/1/test.response") - val attributes = uAttributes { - forRequest(testSource, testSink, UPriority.UPRIORITY_CS4, 3000) + @DisplayName("Test validateSink for a response message where the sink is missing") + fun testUAttributeValidatorValidateSinkResponseMissing() { + val request: UMessage = uMessage { + forRequest(defaultUUri, methodUUri, 1000) + } + val response: UMessage = uMessage { + forResponse(request.attributes) } + val attributes = response.attributes.copy { + clearSink() + } + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) - val validator = attributes.getValidator() - assertEquals("UAttributesValidator.Request", validator.toString()) - val status = validator.validate(attributes) - assertEquals( - "Invalid RPC method uri. Uri should be the method to be called, or method from response.", - status.getMessage() - ) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Response") + assertEquals(result.getMessage(), "Missing Sink") } @Test - @DisplayName("test_valid_response_uri_in_sink") - fun test_valid_response_uri_in_sink() { - val testSink = LongUriSerializer.INSTANCE.deserialize("/test.client/1/rpc.response") - val attributes = uAttributes { - forResponse( - testSource, - testSink, - UPriority.UPRIORITY_CS4, - UUIDV8() - ) + @DisplayName("Test validateSink for a response message where the sink is the default instance") + fun testUAttributeValidatorValidateSinkResponseDefault() { + val request: UMessage = uMessage { + forRequest(defaultUUri, methodUUri, 1000) } - val validator = attributes.getValidator() - assertEquals("UAttributesValidator.Response", validator.toString()) - val status = validator.validate(attributes) - assertEquals(ValidationResult.success(), status) - } - - @Test - @DisplayName("test_invalid_response_uri_in_sink") - fun test_invalid_response_uri_in_sink() { - val testSink = LongUriSerializer.INSTANCE.deserialize("/test.client/1/rpc.method") - val attributes = uAttributes { - forResponse( - testSource, - testSink, - UPriority.UPRIORITY_CS4, - UUIDV8() - ) + val response: UMessage = uMessage { + forResponse(request.attributes) } - val validator = attributes.getValidator() - assertEquals("UAttributesValidator.Response", validator.toString()) - val status = validator.validate(attributes) - assertEquals("Invalid RPC response type.", status.getMessage()) - } - - @Test - @DisplayName("test notification validation with missing sink") - fun test_notification_validation_with_missing_sink() { - val attributes: UAttributes = uAttributes { - forNotification(testSource, UUri.getDefaultInstance(), UPriority.UPRIORITY_CS0) + val attributes = response.attributes.copy { + sink = UUri.getDefaultInstance() } - val validator: UAttributesValidator = Notification - val status = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Missing Sink", status.getMessage()) + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) + + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Response") + assertEquals(result.getMessage(), "Missing Sink") } @Test - @DisplayName("test notification validation using publish validator") - fun test_notification_validation_using_publish_validator() { - val attributes: UAttributes = uAttributes { - forNotification(testSource, testSink, UPriority.UPRIORITY_CS0) + @DisplayName("Test validateSink for a response message where the sink is NOT the defaultResourceId") + fun testUAttributeValidatorValidateSinkResponseDefaultResourceId() { + val request: UMessage = uMessage { + forRequest(methodUUri, defaultUUri, 1000) } - val validator: UAttributesValidator = Publish - val status = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Wrong Attribute Type [UMESSAGE_TYPE_NOTIFICATION]", status.getMessage()) + val response: UMessage = uMessage { + forResponse(request.attributes) + } + val validator: UAttributesValidator = response.attributes.getValidator() + val result = validator.validate(response.attributes) + + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Response") + assertEquals(result.getMessage(), "Invalid Sink Uri") } @Test - @DisplayName("test notification validation when sink is missing") - fun test_notification_validation_when_sink_is_missing() { - val attributes: UAttributes = uAttributes { - id = UUIDV8() - source = testSource - type = UMessageType.UMESSAGE_TYPE_NOTIFICATION - priority = UPriority.UPRIORITY_CS0 + @DisplayName("Test validateReqId for a response message when the reqid is missing") + fun testUAttributeValidatorValidateReqIdMissing() { + val request: UMessage = uMessage { + forRequest(defaultUUri, methodUUri, 1000) + } + val response: UMessage = uMessage { + forResponse(request.attributes) + } + val attributes = response.attributes.copy { + clearReqid() } + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) - val validator: UAttributesValidator = Notification - val status = validator.validate(attributes) - assertTrue(status.isFailure()) - assertEquals("Missing Sink", status.getMessage()) + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Response") + assertEquals(result.getMessage(), "Missing correlationId") } @Test - @DisplayName("test notification validation with a valid notification UAttributes") - fun test_notification_validation_with_a_valid_notification_UAttributes() { - val attributes: UAttributes = uAttributes { - forNotification(testSource, testSink, UPriority.UPRIORITY_CS0) + @DisplayName("Test validateReqId for a response message when the reqid is the default instance") + fun testUAttributeValidatorValidateReqIdDefault() { + val request: UMessage = uMessage { + forRequest(defaultUUri, methodUUri, 1000) } - val validator: UAttributesValidator = Notification - val status = validator.validate(attributes) - assertTrue(status.isSuccess()) - assertEquals("", status.getMessage()) + val response: UMessage = uMessage { + forResponse(request.attributes) + } + val attributes = response.attributes.copy { reqid = UUID.getDefaultInstance() } + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) + + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Response") + assertEquals(result.getMessage(), "Missing correlationId") } - private val testSink = uUri { - authority = uAuthority { name = "vcu.someVin.veh.ultifi.gm.com" } - entity = uEntity { - name = "petapp.ultifi.gm.com" - versionMajor = 1 + @Test + @DisplayName("Test validateReqId for a response message when the reqid not a valid uprotocol UUID") + fun testUAttributeValidatorValidateReqIdInvalid() { + val request: UMessage = uMessage { + forRequest(defaultUUri, methodUUri, 1000) } - resource = uResource { - forRpcResponse() + val response: UMessage = uMessage { + forResponse(request.attributes) } + val attributes = response.attributes.copy { reqid = uUID { + lsb = -0x41524111 + msb = -0x21524111 + } } + val validator: UAttributesValidator = attributes.getValidator() + val result = validator.validate(attributes) + + assertTrue(result.isFailure()) + assertEquals(validator.toString(), "UAttributesValidator.Response") + assertEquals(result.getMessage(), "Invalid correlation UUID") } - private val testSource: UUri = uUri { - entity = uEntity { - name = "hartley_app" - versionMajor = 1 - } - resource = uResource { - forRpcResponse() + @Test + @DisplayName("Test getValidator for other type of messages") + fun testUAttributeValidatorGetValidatorForOtherTypes() { + val attributes1 = uAttributes { + type = UMessageType.UMESSAGE_TYPE_UNSPECIFIED } + val validator1: UAttributesValidator = attributes1.getValidator() + + assertTrue(validator1 is Publish) } } diff --git a/src/test/kotlin/org/eclipse/uprotocol/uri/UUriExamples.kt b/src/test/kotlin/org/eclipse/uprotocol/uri/UUriExamples.kt new file mode 100644 index 0000000..86afd40 --- /dev/null +++ b/src/test/kotlin/org/eclipse/uprotocol/uri/UUriExamples.kt @@ -0,0 +1,62 @@ +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.uprotocol.uri + +import org.eclipse.uprotocol.core.usubscription.v3.USubscriptionProto +import org.eclipse.uprotocol.uri.factory.UUriFactory +import org.eclipse.uprotocol.uri.serializer.deserializeAsUUri +import org.eclipse.uprotocol.uri.serializer.serialize +import org.eclipse.uprotocol.uri.validator.* +import org.eclipse.uprotocol.v1.UUri +import org.eclipse.uprotocol.v1.uUri +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test + +class UUriExamples { + @Test + fun example_UriFactory_FromProto() { + // Fetch the notification topic Uri from the USubscriptionProto generated code + val uri: UUri = UUriFactory.fromProto(USubscriptionProto.getDescriptor().services[0], 0) + + assertEquals(uri.ueId, 0) + assertEquals(uri.ueVersionMajor, 3) + assertEquals(uri.resourceId, 0) + } + + @Test + fun example_Serializer_Deserializer() { + val uri = uUri { + ueId = 1 + ueVersionMajor = 2 + resourceId = 3 + } + val strUri: String = uri.serialize() + assertEquals("/1/2/3", strUri) + assertEquals(uri, strUri.deserializeAsUUri()) + } + + @Test + fun example_UriValidator() { + val uri = uUri { + ueId = 1 + ueVersionMajor = 2 + resourceId = 3 + } + assertFalse(uri.isEmpty()) + assertFalse(uri.isDefaultResourceId()) + assertTrue(uri.isRpcMethod()) + assertFalse(uri.isRpcResponse()) + assertFalse(uri.isTopic()) + } +} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/uri/factory/UEntityFactoryTest.kt b/src/test/kotlin/org/eclipse/uprotocol/uri/factory/UEntityFactoryTest.kt deleted file mode 100644 index c4b24f7..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/uri/factory/UEntityFactoryTest.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package org.eclipse.uprotocol.uri.factory - -import org.eclipse.uprotocol.core.udiscovery.v3.UDiscoveryProto -import org.eclipse.uprotocol.core.usubscription.v3.USubscriptionProto -import org.eclipse.uprotocol.core.utwin.v2.UTwinProto -import org.eclipse.uprotocol.uri.factory.UEntityFactory.fromProto -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test - - -class UEntityFactoryTest{ - @Test - @DisplayName("Test build valid usubscription UEntity") - fun test_build_valid_usubscription_uentity() { - val descriptor = USubscriptionProto.getDescriptor().services[0] - val entity = fromProto(descriptor) - - assertEquals(entity.name, "core.usubscription") - assertEquals(entity.id, 0) - assertEquals(entity.versionMajor, 3) - assertEquals(entity.versionMinor, 0) - } - - @Test - @DisplayName("Test build valid uDiscovery UEntity") - fun test_build_valid_udiscovery_uentity() { - val descriptor = UDiscoveryProto.getDescriptor().services[0] - - val entity = fromProto(descriptor) - - assertEquals(entity.name, "core.udiscovery") - assertEquals(entity.id, 1) - assertEquals(entity.versionMajor, 3) - assertEquals(entity.versionMinor, 0) - } - - @Test - @DisplayName("Test build valid uTwin UEntity") - fun test_build_valid_utwin_uentity() { - val descriptor = UTwinProto.getDescriptor().services[0] - - val entity = fromProto(descriptor) - - assertEquals(entity.name, "core.utwin") - assertEquals(entity.id, 26) - assertEquals(entity.versionMajor, 2) - assertEquals(entity.versionMinor, 0) - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/uri/factory/UUriFactoryTest.kt b/src/test/kotlin/org/eclipse/uprotocol/uri/factory/UUriFactoryTest.kt new file mode 100644 index 0000000..89d1216 --- /dev/null +++ b/src/test/kotlin/org/eclipse/uprotocol/uri/factory/UUriFactoryTest.kt @@ -0,0 +1,72 @@ +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.uprotocol.uri.factory + +import org.eclipse.uprotocol.core.usubscription.v3.USubscriptionProto +import org.eclipse.uprotocol.v1.UUri +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class UUriFactoryTest { + @Test + @DisplayName("Test fromProto") + fun testFromProto() { + val uri: UUri = UUriFactory.fromProto( + USubscriptionProto.getDescriptor().services[0], 0 + ) + + assertEquals(uri.authorityName, "") + assertEquals(uri.ueId, 0) + assertEquals(uri.ueVersionMajor, 3) + assertEquals(uri.resourceId, 0) + } + + @Test + @DisplayName("Test ANY") + fun testAny() { + val uri: UUri = UUriFactory.ANY + + assertEquals(uri.authorityName, "*") + assertEquals(uri.ueId, 65535) + assertEquals(uri.ueVersionMajor, 255) + assertEquals(uri.resourceId, 65535) + } + + @Test + @DisplayName("Test fromProto with authority name") + fun testFromProtoWithAuthorityName() { + val uri: UUri = UUriFactory.fromProto( + USubscriptionProto.getDescriptor().services[0], 0, "hartley" + ) + + assertEquals(uri.authorityName, "hartley") + assertEquals(uri.ueId, 0) + assertEquals(uri.ueVersionMajor, 3) + assertEquals(uri.resourceId, 0) + } + + @Test + @DisplayName("Test fromProto with empty authority name string") + fun testFromProtoWithEmptyAuthorityName() { + val uri: UUri = UUriFactory.fromProto( + USubscriptionProto.getDescriptor().services[0], 0, "" + ) + + assertEquals(uri.authorityName, "") + assertEquals(uri.ueId, 0) + assertEquals(uri.ueVersionMajor, 3) + assertEquals(uri.resourceId, 0) + } +} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/IpAddressTest.kt b/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/IpAddressTest.kt deleted file mode 100644 index 0ce93d8..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/IpAddressTest.kt +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package org.eclipse.uprotocol.uri.serializer - -import org.eclipse.uprotocol.uri.serializer.IpAddress.isValid -import org.eclipse.uprotocol.uri.serializer.IpAddress.toBytes -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import java.net.InetAddress - -class IpAddressTest { - @Test - @DisplayName("Test toBytes with empty ipAddress") - fun testToBytesWithEmptyIpAddress() { - val bytes = toBytes("") - assertEquals(0, bytes.size) - } - - @Test - @DisplayName("Test toBytes with invalid ipAddress") - fun testToBytesWithInvalidIpAddress() { - val bytes = toBytes("invalid") - assertEquals(0, bytes.size) - } - - @Test - @DisplayName("Test toBytes with valid IPv4 address") - fun testToBytesWithValidIPv4Address() { - val bytes = toBytes("192.168.1.100") - assertEquals(4, bytes.size) - assertEquals(192, java.lang.Byte.toUnsignedInt(bytes[0])) - assertEquals(168, java.lang.Byte.toUnsignedInt(bytes[1])) - assertEquals(1, java.lang.Byte.toUnsignedInt(bytes[2])) - assertEquals(100, java.lang.Byte.toUnsignedInt(bytes[3])) - } - - @Test - @DisplayName("Test toBytes with valid IPv6 address") - fun testToBytesWithValidIPv6Address() { - val bytes = toBytes("2001:db8:85a3:0:0:8a2e:370:7334") - - assertEquals(InetAddress.getByAddress(bytes).hostAddress, "2001:db8:85a3:0:0:8a2e:370:7334") - } - - @Test - @DisplayName("Test isValid with empty ipAddress") - fun testIsValidWithEmptyIpAddress() { - assertFalse(isValid("")) - } - - @Test - @DisplayName("Test isValid with invalid ipAddress") - fun testIsValidWithInvalidIpAddress() { - assertFalse(isValid("invalid")) - } - - @Test - @DisplayName("Test isValid with valid IPv4 address") - fun testIsValidWithValidIPv4Address() { - assertTrue(isValid("192.168.1.100")) - } - - @Test - @DisplayName("Test isValid with valid IPv6 address") - fun testIsValidWithValidIPv6Address() { - assertTrue(isValid("2001:db8:85a3:0:0:8a2e:370:7334")) - } - - @Test - @DisplayName("Test isValid with invalid IPv4 address") - fun testIsValidWithInvalidIPv4Address() { - val bytes = toBytes("192.168.1.2586") - assertEquals(0, bytes.size) - assertFalse(isValid("192.168.1.2586")) - } - - @Test - @DisplayName("Test isValid with invalid IPv4 passing large number") - fun testIsValidWithInvalidIPv4PassingLargeNumber() { - val ipString = "2875687346587326457836485623874658723645782364875623847562378465.1.1.abc" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid IPv4 passing negative value") - fun testIsValidWithInvalidIPv4PassingNegative() { - val ipString = "-1.1.1.abc" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid IPv4 passing charaters") - fun testIsValidWithInvalidIPv4PassingCharacters() { - val ipString = "1.1.1.abc" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid IPv6 address") - fun testIsValidWithInvalidIPv6Address() { - val ipString = "ZX1:db8::" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid IPv6 address passing weird values") - fun testIsValidWithInvalidIPv6AddressPassingWeirdValues() { - val ipString = "-1:ZX1:db8::" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid IPv6 address that has way too many groups") - fun testIsValidWithInvalidIPv6AddressThatHasWayTooManyGroups() { - val ipString = "2001:db8:85a3:0:0:8a2e:370:7334:1234" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with valid IPv6 address that has 8 groups") - fun testIsValidWithValidIPv6AddressThatHas8Groups() { - val ipString = "2001:db8:85a3:0:0:8a2e:370:7334" - val bytes = toBytes(ipString) - assertEquals(16, bytes.size) - assertTrue(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid IPv6 address with too many empty groups") - fun testIsValidWithInvalidIPv6AddressWithTooManyEmptyGroups() { - val ipString = "2001::85a3::8a2e::7334" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with valid IPv6 address with one empty group") - fun testIsValidWithValidIPv6AddressWithOneEmptyGroup() { - val ipString = "2001:db8:85a3::8a2e:370:7334" - val bytes = toBytes(ipString) - assertEquals(16, bytes.size) - assertTrue(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid IPv6 address that ends with a colon") - fun testIsValidWithInvalidIPv6AddressThatEndsWithAColon() { - val ipString = "2001:db8:85a3::8a2e:370:7334:" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid IPv6 address that has doesn't have double colon and not enough groups") - fun testIsValidWithInvalidIPv6AddressThatHasDoesntHaveDoubleColonAndNotEnoughGroups() { - val ipString = "2001:db8:85a3:0:0:8a2e:370" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with valid IPv6 address that ends with double colons") - fun testIsValidWithValidIPv6AddressThatEndsWithDoubleColons() { - val ipString = "2001:db8:85a3:8a2e::" - val bytes = toBytes(ipString) - assertEquals(16, bytes.size) - assertTrue(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with all number values") - fun testIsValidWithAllNumberValues() { - val ipString = "123:456:7890::" - val bytes = toBytes(ipString) - assertEquals(16, bytes.size) - assertTrue(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with valid lowercase hexidecimal letters") - fun testIsValidWithValidLowercaseHexidecimalLetters() { - val ipString = "abcd:ef12:3456::" - val bytes = toBytes(ipString) - assertEquals(16, bytes.size) - assertTrue(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with valid uppercase hexidecimal letters") - fun testIsValidWithValidUppercaseHexidecimalLetters() { - val ipString = "ABCD:EF12:3456::" - val bytes = toBytes(ipString) - assertEquals(16, bytes.size) - assertTrue(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid uppercase hexidecimal letters") - fun testIsValidWithInvalidUppercaseHexidecimalLetters() { - val ipString = "ABCD:EFG2:3456::" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid hexidecimal letters") - fun testIsValidWithInvalidLowercaseHexidecimalLetters() { - val ipString = "-C=[]:E{12g:3456" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid digit") - fun testIsValidWithInvalidDigit1() { - val ipString = "aC=[]:E{12g:3456" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid digit") - fun testIsValidWithInvalidDigit2() { - val ipString = "aCd[:E{12g:3456" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid digit") - fun testIsValidWithInvalidDigit3() { - val ipString = "aCd:E{2g:3456" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid IPv6 address that has double colon and 8 groups") - fun testIsValidWithInvalidIPv6AddressThatHasDoubleColonAnd8Groups() { - val ipString = "dead:beef:85a3::0:0:8a2e:370" - val bytes = toBytes(ipString) - assertEquals(16, bytes.size) - assertTrue(isValid(ipString)) - } - - @Test - @DisplayName("Test isValid with invalid IPv6 address that has only has 7 groups") - fun testIsValidWithInvalidIPv6AddressThatHasOnlyHas7Groups() { - val ipString = "dead:beef:85a3:0:0:8a2e:370" - val bytes = toBytes(ipString) - assertEquals(0, bytes.size) - assertFalse(isValid(ipString)) - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/LongUriSerializerTest.kt b/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/LongUriSerializerTest.kt deleted file mode 100644 index 9f20b62..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/LongUriSerializerTest.kt +++ /dev/null @@ -1,1009 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package org.eclipse.uprotocol.uri.serializer - -import org.eclipse.uprotocol.uri.validator.isEmpty -import org.eclipse.uprotocol.uri.validator.isRemote -import org.eclipse.uprotocol.v1.* -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test - - -class LongUriSerializerTest { - @Test - @DisplayName("Test using the serializers") - fun test_using_the_serializers() { - val uri: UUri = uUri { - entity = uEntity { name = "hartley" } - resource = uResource { forRpcRequest("raise") } - } - - val strUri: String = LongUriSerializer.INSTANCE.serialize(uri) - assertEquals("/hartley//rpc.raise", strUri) - val uri2: UUri = LongUriSerializer.INSTANCE.deserialize(strUri) - assertEquals(uri, uri2) - } - - @Test - @DisplayName("Test parse uProtocol uri that is empty string") - fun test_parse_protocol_uri_when_is_empty_string() { - val uri = "" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with schema and slash") - fun test_parse_protocol_uri_with_schema_and_slash() { - val uri = "/" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.hasAuthority()) - assertTrue(uuri.isEmpty()) - assertFalse(uuri.hasResource()) - assertFalse(uuri.hasEntity()) - val uri2: String = LongUriSerializer.INSTANCE.serialize(uUri { }) - assertTrue(uri2.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with schema and double slash") - fun test_parse_protocol_uri_with_schema_and_double_slash() { - val uri = "//" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.hasAuthority()) - assertFalse(uuri.hasResource()) - assertFalse(uuri.hasEntity()) - assertTrue(uuri.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with schema and 3 slash and something") - fun test_parse_protocol_uri_with_schema_and_3_slash_and_something() { - val uri = "///body.access" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.hasAuthority()) - assertFalse(uuri.hasResource()) - assertFalse(uuri.hasEntity()) - assertTrue(uuri.isEmpty()) - assertNotEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - } - - @Test - @DisplayName("Test parse uProtocol uri with schema and 4 slash and something") - fun test_parse_protocol_uri_with_schema_and_4_slash_and_something() { - val uri = "////body.access" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertFalse(uuri.hasResource()) - assertFalse(uuri.hasEntity()) - assertTrue(uuri.entity.name.isBlank()) - assertEquals(0, uuri.entity.versionMajor) - } - - @Test - @DisplayName("Test parse uProtocol uri with schema and 5 slash and something") - fun test_parse_protocol_uri_with_schema_and_5_slash_and_something() { - val uri = "/////body.access" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertFalse(uuri.hasResource()) - assertFalse(uuri.hasEntity()) - assertTrue(uuri.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with schema and 6 slash and something") - fun test_parse_protocol_uri_with_schema_and_6_slash_and_something() { - val uri = "//////body.access" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertTrue(uuri.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with local service no version") - fun test_parse_protocol_uri_with_local_service_no_version() { - val uri = "/body.access" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals(0, uuri.entity.versionMinor) - assertFalse(uuri.hasResource()) - } - - @Test - @DisplayName("Test parse uProtocol uri with local service with version") - fun test_parse_protocol_uri_with_local_service_with_version() { - val uri = "/body.access/1" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertEquals("body.access", uuri.entity.name) - assertEquals(1, uuri.entity.versionMajor) - assertFalse(uuri.hasResource()) - } - - @Test - @DisplayName("Test parse uProtocol uri with local service no version with resource name only") - fun test_parse_protocol_uri_with_local_service_no_version_with_resource_name_only() { - val uri = "/body.access//door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals(0, uuri.entity.versionMinor) - assertEquals("door", uuri.resource.name) - assertTrue(uuri.resource.instance.isEmpty()) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with local service with version with resource name only") - fun test_parse_protocol_uri_with_local_service_with_version_with_resource_name_only() { - val uri = "/body.access/1/door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertEquals("body.access", uuri.entity.name) - assertEquals(1, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertTrue(uuri.resource.instance.isEmpty()) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with local service no version with resource and instance only") - fun test_parse_protocol_uri_with_local_service_no_version_with_resource_with_instance() { - val uri = "/body.access//door.front_left" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("front_left", uuri.resource.instance) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with local service with version with resource and instance only") - fun test_parse_protocol_uri_with_local_service_with_version_with_resource_with_getMessage() { - val uri = "/body.access/1/door.front_left" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertEquals("body.access", uuri.entity.name) - assertNotEquals(0, uuri.entity.versionMajor) - assertEquals(1, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("front_left", uuri.resource.instance) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with local service no version with resource with instance and message") - fun test_parse_protocol_uri_with_local_service_no_version_with_resource_with_instance_and_getMessage() { - val uri = "/body.access//door.front_left#Door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("front_left", uuri.resource.instance) - assertFalse(uuri.resource.message.isEmpty()) - assertEquals("Door", uuri.resource.message) - } - - @Test - @DisplayName("Test parse uProtocol uri with local service with version with resource with instance and message") - fun test_parse_protocol_uri_with_local_service_with_version_with_resource_with_instance_and_getMessage() { - val uri = "/body.access/1/door.front_left#Door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertEquals("body.access", uuri.entity.name) - assertNotEquals(0, uuri.entity.versionMajor) - assertEquals(1, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("front_left", uuri.resource.instance) - assertFalse(uuri.resource.message.isEmpty()) - assertEquals("Door", uuri.resource.message) - } - - @Test - @DisplayName("Test parse uProtocol RPC uri with local service no version") - fun test_parse_protocol_rpc_uri_with_local_service_no_version() { - val uri = "/petapp//rpc.response" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertEquals("petapp", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals("rpc", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("response", uuri.resource.instance) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol RPC uri with local service with version") - fun test_parse_protocol_rpc_uri_with_local_service_with_version() { - val uri = "/petapp/1/rpc.response" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertEquals("petapp", uuri.entity.name) - assertNotEquals(0, uuri.entity.versionMajor) - assertEquals(1, uuri.entity.versionMajor) - assertEquals("rpc", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("response", uuri.resource.instance) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote service , name with device and domain") - fun test_parse_protocol_uri_with_remote_service_only_device_and_domain() { - val uri = "//VCU.MY_CAR_VIN" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("VCU.MY_CAR_VIN", uuri.authority.name) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote service only device and cloud domain") - fun test_parse_protocol_uri_with_remote_service_only_device_and_cloud_domain() { - val uri = "//cloud.uprotocol.example.com" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("cloud.uprotocol.example.com", uuri.authority.name) - assertFalse(uuri.hasEntity()) - assertFalse(uuri.hasResource()) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote service no version") - fun test_parse_protocol_uri_with_remote_service_no_version() { - val uri = "//VCU.MY_CAR_VIN/body.access" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("VCU.MY_CAR_VIN", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertFalse(uuri.hasResource()) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote cloud service no version") - fun test_parse_protocol_uri_with_remote_cloud_service_no_version() { - val uri = "//cloud.uprotocol.example.com/body.access" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("cloud.uprotocol.example.com", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertFalse(uuri.hasResource()) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote service with version") - fun test_parse_protocol_uri_with_remote_service_with_version() { - val uri = "//VCU.MY_CAR_VIN/body.access/1" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("VCU.MY_CAR_VIN", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertNotEquals(0, uuri.entity.versionMajor) - assertEquals(1, uuri.entity.versionMajor) - assertFalse(uuri.hasResource()) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote cloud service with version") - fun test_parse_protocol_uri_with_remote_cloud_service_with_version() { - val uri = "//cloud.uprotocol.example.com/body.access/1" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("cloud.uprotocol.example.com", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertNotEquals(0, uuri.entity.versionMajor) - assertEquals(1, uuri.entity.versionMajor) - assertFalse(uuri.hasResource()) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote service no version with resource name only") - fun test_parse_protocol_uri_with_remote_service_no_version_with_resource_name_only() { - val uri = "//VCU.MY_CAR_VIN/body.access//door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("VCU.MY_CAR_VIN", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertTrue(uuri.resource.instance.isEmpty()) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote cloud service no version with resource name only") - fun test_parse_protocol_uri_with_remote_cloud_service_no_version_with_resource_name_only() { - val uri = "//cloud.uprotocol.example.com/body.access//door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("cloud.uprotocol.example.com", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertTrue(uuri.resource.instance.isEmpty()) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote service with version with resource name only") - fun test_parse_protocol_uri_with_remote_service_with_version_with_resource_name_only() { - val uri = "//VCU.MY_CAR_VIN/body.access/1/door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("VCU.MY_CAR_VIN", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertNotEquals(0, uuri.entity.versionMajor) - assertEquals(1, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertTrue(uuri.resource.instance.isEmpty()) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote cloud service with version with resource name only") - fun test_parse_protocol_uri_with_remote_service_cloud_with_version_with_resource_name_only() { - val uri = "//cloud.uprotocol.example.com/body.access/1/door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("cloud.uprotocol.example.com", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertNotEquals(0, uuri.entity.versionMajor) - assertEquals(1, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertTrue(uuri.resource.instance.isEmpty()) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote service no version with resource and instance no message") - fun test_parse_protocol_uri_with_remote_service_no_version_with_resource_and_instance_no_getMessage() { - val uri = "//VCU.MY_CAR_VIN/body.access//door.front_left" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("VCU.MY_CAR_VIN", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("front_left", uuri.resource.instance) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote service with version with resource and instance no message") - fun test_parse_protocol_uri_with_remote_service_with_version_with_resource_and_instance_no_getMessage() { - val uri = "//VCU.MY_CAR_VIN/body.access/1/door.front_left" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("VCU.MY_CAR_VIN", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertNotEquals(0, uuri.entity.versionMajor) - assertEquals(1, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("front_left", uuri.resource.instance) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote service no version with resource and instance and message") - fun test_parse_protocol_uri_with_remote_service_no_version_with_resource_and_instance_and_getMessage() { - val uri = "//VCU.MY_CAR_VIN/body.access//door.front_left#Door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("VCU.MY_CAR_VIN", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("front_left", uuri.resource.instance) - assertFalse(uuri.resource.message.isEmpty()) - assertEquals("Door", uuri.resource.message) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote cloud service no version with resource and instance and message") - fun test_parse_protocol_uri_with_remote_cloud_service_no_version_with_resource_and_instance_and_getMessage() { - val uri = "//cloud.uprotocol.example.com/body.access//door.front_left#Door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("cloud.uprotocol.example.com", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("front_left", uuri.resource.instance) - assertFalse(uuri.resource.message.isEmpty()) - assertEquals("Door", uuri.resource.message) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote service with version with resource and instance and message") - fun test_parse_protocol_uri_with_remote_service_with_version_with_resource_and_instance_and_getMessage() { - val uri = "//VCU.MY_CAR_VIN/body.access/1/door.front_left#Door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("VCU.MY_CAR_VIN", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertNotEquals(0, uuri.entity.versionMajor) - assertEquals(1, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("front_left", uuri.resource.instance) - assertFalse(uuri.resource.message.isEmpty()) - assertEquals("Door", uuri.resource.message) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote cloud service with version with resource and instance and message") - fun test_parse_protocol_uri_with_remote_cloud_service_with_version_with_resource_and_instance_and_getMessage() { - val uri = "//cloud.uprotocol.example.com/body.access/1/door.front_left#Door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("cloud.uprotocol.example.com", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("body.access", uuri.entity.name) - assertNotEquals(0, uuri.entity.versionMajor) - assertEquals(1, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("front_left", uuri.resource.instance) - assertFalse(uuri.resource.message.isEmpty()) - assertEquals("Door", uuri.resource.message) - } - - @Test - @DisplayName("Test parse uProtocol uri with microRemote service with version with resource with message when there is only device, no domain") - fun test_parse_protocol_uri_with_remote_service_with_version_with_resource_with_message_device_no_domain() { - val uri = "//VCU/body.access/1/door.front_left" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("VCU", uuri.authority.name) - assertFalse(uuri.authority.name.isEmpty()) - assertEquals("body.access", uuri.entity.name) - assertNotEquals(0, uuri.entity.versionMajor) - assertEquals(1, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("front_left", uuri.resource.instance) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol RPC uri with microRemote service no version") - fun test_parse_protocol_rpc_uri_with_remote_service_no_version() { - val uri = "//bo.cloud/petapp//rpc.response" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("bo.cloud", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("petapp", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals("rpc", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("response", uuri.resource.instance) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test parse uProtocol RPC uri with microRemote service with version") - fun test_parse_protocol_rpc_uri_with_remote_service_with_version() { - val uri = "//bo.cloud/petapp/1/rpc.response" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("bo.cloud", uuri.authority.name) - assertFalse(uuri.authority.name.isBlank()) - assertEquals("petapp", uuri.entity.name) - assertNotEquals(0, uuri.entity.versionMajor) - assertEquals(1, uuri.entity.versionMajor) - assertEquals("rpc", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("response", uuri.resource.instance) - assertTrue(uuri.resource.message.isEmpty()) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an empty URI Object") - fun test_build_protocol_uri_from__uri_when__uri_isEmpty() { - val uuri: UUri = uUri { } - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI object with an empty USE") - fun test_build_protocol_uri_from__uri_when__uri_has_empty_use() { - val use: UEntity = uEntity { } - val uuri: UUri = uUri { - authority = uAuthority { } - entity = use - resource = uResource { name = "door" } - } - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("/////door", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a local authority with service no version") - fun test_build_protocol_uri_from__uri_when__uri_has_local_authority_service_no_version() { - val uuri: UUri = uUri { entity = uEntity { name = "body.access" } } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("/body.access", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a local authority with service and version") - fun test_build_protocol_uri_from__uri_when__uri_has_local_authority_service_and_version() { - val use: UEntity = uEntity { - name = "body.access" - versionMajor = 1 - } - val uuri: UUri = uUri { - entity = use - resource = uResource { } - } - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("/body.access/1", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a local authority with service no version with resource") - fun test_build_protocol_uri_from__uri_when__uri_has_local_authority_service_no_version_with_resource() { - val use: UEntity = uEntity { - name = "body.access" - } - val uuri: UUri = uUri { - entity = use - resource = uResource { name = "door" } - } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("/body.access//door", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a local authority with service and version with resource") - fun test_build_protocol_uri_from__uri_when__uri_has_local_authority_service_and_version_with_resource() { - val use: UEntity = uEntity { - name = "body.access" - versionMajor = 1 - } - val uuri: UUri = uUri { - entity = use - resource = uResource { name = "door" } - } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("/body.access/1/door", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a local authority with service no version with resource with instance no message") - fun test_build_protocol_uri_from__uri_when__uri_has_local_authority_service_no_version_with_resource_with_instance_no_getMessage() { - val use: UEntity = uEntity { - name = "body.access" - } - val uuri: UUri = uUri { - entity = use - resource = uResource { - name = "door" - instance = "front_left" - } - } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("/body.access//door.front_left", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a local authority with service and version with resource with instance no message") - fun test_build_protocol_uri_from__uri_when__uri_has_local_authority_service_and_version_with_resource_with_instance_no_getMessage() { - val use: UEntity = uEntity { - name = "body.access" - versionMajor = 1 - } - val uuri: UUri = uUri { - entity = use - resource = uResource { - name = "door" - instance = "front_left" - } - } - - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("/body.access/1/door.front_left", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a local authority with service no version with resource with instance and message") - fun test_build_protocol_uri_from__uri_when__uri_has_local_authority_service_no_version_with_resource_with_instance_with_getMessage() { - val use: UEntity = uEntity { - name = "body.access" - } - val uuri: UUri = uUri { - entity = use - resource = uResource { - name = "door" - instance = "front_left" - message = "Door" - } - } - - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("/body.access//door.front_left#Door", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a local authority with service and version with resource with instance and message") - fun test_build_protocol_uri_from__uri_when__uri_has_local_authority_service_and_version_with_resource_with_instance_with_getMessage() { - val use: UEntity = uEntity { - name = "body.access" - versionMajor = 1 - } - val uuri: UUri = uUri { - entity = use - resource = uResource { - name = "door" - instance = "front_left" - message = "Door" - } - } - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("/body.access/1/door.front_left#Door", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a microRemote authority with service no version") - fun test_build_protocol_uri_from__uri_when__uri_has_remote_authority_service_no_version() { - val use: UEntity = uEntity { name = "body.access" } - val uuri: UUri = uUri { - authority = uAuthority { name = "vcu.my_car_vin" } - entity = use - } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("//vcu.my_car_vin/body.access", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a microRemote authority with service and version") - fun test_build_protocol_uri_from__uri_when__uri_has_remote_authority_service_and_version() { - val use: UEntity = uEntity { - name = "body.access" - versionMajor = 1 - } - val uuri: UUri = uUri { - entity = use - authority = uAuthority { - name = "vcu.my_car_vin" - - } - } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("//vcu.my_car_vin/body.access/1", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a microRemote cloud authority with service and version") - fun test_build_protocol_uri_from__uri_when__uri_has_remote_cloud_authority_service_and_version() { - val use: UEntity = uEntity { - name = "body.access" - versionMajor = 1 - } - val uuri: UUri = uUri { - authority = uAuthority { name = "cloud.uprotocol.example.com" } - entity = use - } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("//cloud.uprotocol.example.com/body.access/1", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a microRemote authority with service and version with resource") - fun test_build_protocol_uri_from__uri_when__uri_has_remote_authority_service_and_version_with_resource() { - val use: UEntity = uEntity { - name = "body.access" - versionMajor = 1 - } - val uuri: UUri = uUri { - authority = uAuthority { name = "vcu.my_car_vin" } - entity = use - resource = uResource { name = "door" } - } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("//vcu.my_car_vin/body.access/1/door", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a microRemote authority with service no version with resource") - fun test_build_protocol_uri_from__uri_when__uri_has_remote_authority_service_no_version_with_resource() { - val use: UEntity = uEntity { - name = "body.access" - - } - val uuri: UUri = uUri { - authority = uAuthority { name = "vcu.my_car_vin" } - entity = use - resource = uResource { name = "door" } - } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("//vcu.my_car_vin/body.access//door", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a microRemote authority with service and version with resource with instance no message") - fun test_build_protocol_uri_from__uri_when__uri_has_remote_authority_service_and_version_with_resource_with_instance_no_getMessage() { - val use: UEntity = uEntity { - name = "body.access" - versionMajor = 1 - } - val uuri: UUri = uUri { - authority = uAuthority { name = "vcu.my_car_vin" } - entity = use - resource = uResource { - name = "door" - instance = "front_left" - } - } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("//vcu.my_car_vin/body.access/1/door.front_left", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a microRemote cloud authority with service and version with resource with instance no message") - fun test_build_protocol_uri_from__uri_when__uri_has_remote_cloud_authority_service_and_version_with_resource_with_instance_no_getMessage() { - val use: UEntity = uEntity { - name = "body.access" - versionMajor = 1 - } - val uuri: UUri = uUri { - authority = uAuthority { name = "cloud.uprotocol.example.com" } - entity = use - resource = uResource { - name = "door" - instance = "front_left" - } - } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("//cloud.uprotocol.example.com/body.access/1/door.front_left", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a microRemote authority with service no version with resource with instance no message") - fun test_build_protocol_uri_from__uri_when__uri_has_remote_authority_service_no_version_with_resource_with_instance_no_getMessage() { - val use: UEntity = uEntity { - name = "body.access" - } - val uuri: UUri = uUri { - authority = uAuthority { name = "vcu.my_car_vin" } - entity = use - resource = uResource { - name = "door" - instance = "front_left" - } - } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("//vcu.my_car_vin/body.access//door.front_left", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a microRemote authority with service and version with resource with instance and message") - fun test_build_protocol_uri_from__uri_when__uri_has_remote_authority_service_and_version_with_resource_with_instance_and_getMessage() { - val use: UEntity = uEntity { - name = "body.access" - versionMajor = 1 - } - val uuri: UUri = uUri { - authority = uAuthority { name = "vcu.my_car_vin" } - entity = use - resource = uResource { - name = "door" - instance = "front_left" - message = "Door" - } - } - - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("//vcu.my_car_vin/body.access/1/door.front_left#Door", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from an URI Object with a microRemote authority with service no version with resource with instance and message") - fun test_build_protocol_uri_from__uri_when__uri_has_remote_authority_service_no_version_with_resource_with_instance_and_getMessage() { - val use: UEntity = uEntity { - name = "body.access" - } - val uuri: UUri = uUri { - authority = uAuthority { name = "vcu.my_car_vin" } - entity = use - resource = uResource { - name = "door" - instance = "front_left" - message = "Door" - } - } - - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uuri) - assertEquals("//vcu.my_car_vin/body.access//door.front_left#Door", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI for the source part of an RPC request, where the source is local") - fun test_build_protocol_uri_for_source_part_of_rpc_request_where_source_is_local() { - val use: UEntity = uEntity { - name = "petapp" - versionMajor = 1 - } - val uResource: UResource = uResource { - name = "rpc" - instance = "response" - } - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uUri { - entity = use - resource = uResource - }) - assertEquals("/petapp/1/rpc.response", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI for the source part of an RPC request, where the source is microRemote") - fun test_build_protocol_uri_for_source_part_of_rpc_request_where_source_is_remote() { - val uAuthority: UAuthority = uAuthority { name = "cloud.uprotocol.example.com" } - val use: UEntity = uEntity { - name = "petapp" - } - val uResource: UResource = uResource { - name = "rpc" - instance = "response" - } - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uUri { - authority = uAuthority - entity = use - resource = uResource - }) - assertEquals("//cloud.uprotocol.example.com/petapp//rpc.response", uProtocolUri) - } - - @Test - @DisplayName("Test Create a uProtocol URI from the parts of URI Object with a microRemote authority with service and version with resource") - fun test_build_protocol_uri_from__uri_parts_when__uri_has_remote_authority_service_and_version_with_resource() { - val uAuthority: UAuthority = uAuthority { name = "vcu.my_car_vin" } - val use: UEntity = uEntity { - name = "body.access" - versionMajor = 1 - } - val uResource: UResource = uResource { name = "door" } - val uProtocolUri: String = LongUriSerializer.INSTANCE.serialize(uUri { - authority = uAuthority - entity = use - resource = uResource - }) - assertEquals("//vcu.my_car_vin/body.access/1/door", uProtocolUri) - } - - @Test - @DisplayName("Test Create a custom URI using no scheme") - fun test_custom_scheme_no_scheme() { - val uAuthority: UAuthority = uAuthority { name = "vcu.my_car_vin" } - val use: UEntity = uEntity { - name = "body.access" - versionMajor = 1 - } - val uResource: UResource = uResource { name = "door" } - val ucustomUri: String = LongUriSerializer.INSTANCE.serialize(uUri { - authority = uAuthority - entity = use - resource = uResource - } - - ) - assertEquals("//vcu.my_car_vin/body.access/1/door", ucustomUri) - } - - @Test - @DisplayName("Test parse local uProtocol uri with custom scheme") - fun test_parse_local_protocol_uri_with_custom_scheme() { - val uri = "custom:/body.access//door.front_left#Door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertFalse(uuri.authority.isRemote()) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertFalse(uuri.resource.instance.isEmpty()) - assertEquals("front_left", uuri.resource.instance) - assertFalse(uuri.resource.message.isEmpty()) - assertEquals("Door", uuri.resource.message) - } - - @Test - @DisplayName("Test parse microRemote uProtocol uri with custom scheme") - fun test_parse_remote_protocol_uri_with_custom_scheme() { - val uri = "custom://vcu.vin/body.access//door.front_left#Door" - val uri2 = "//vcu.vin/body.access//door.front_left#Door" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - assertTrue(uuri.authority.isRemote()) - assertEquals("vcu.vin", uuri.authority.name) - assertEquals("body.access", uuri.entity.name) - assertEquals(0, uuri.entity.versionMajor) - assertEquals("door", uuri.resource.name) - assertEquals("front_left", uuri.resource.instance) - assertEquals("Door", uuri.resource.message) - assertEquals(uri2, LongUriSerializer.INSTANCE.serialize(uuri)) - } -} diff --git a/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/MicroUriSerializerTest.kt b/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/MicroUriSerializerTest.kt deleted file mode 100644 index 7e8d632..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/MicroUriSerializerTest.kt +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package org.eclipse.uprotocol.uri.serializer - -import com.google.protobuf.ByteString -import org.eclipse.uprotocol.uri.serializer.IpAddress.toBytes -import org.eclipse.uprotocol.uri.validator.isEmpty -import org.eclipse.uprotocol.uri.validator.isMicroForm -import org.eclipse.uprotocol.v1.* -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import java.net.InetAddress -import java.net.UnknownHostException - - -class MicroUriSerializerTest { - @Test - @DisplayName("Test serialize and deserialize empty content") - fun test_empty() { - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(UUri.getDefaultInstance()) - assertEquals(bytes.size, 0) - val uri2: UUri = MicroUriSerializer.INSTANCE.deserialize(bytes) - assertTrue(uri2.isEmpty()) - } - - @Test - @DisplayName("Test happy path Byte serialization of local UUri") - fun test_serialize_uri() { - val uri: UUri = uUri { - entity = uEntity { - id = 29999 - versionMajor = 254 - } - resource = uResource { from(19999) } - } - - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(uri) - val uri2: UUri = MicroUriSerializer.INSTANCE.deserialize(bytes) - assertTrue(uri.isMicroForm()) - assertTrue(bytes.isNotEmpty()) - assertEquals(uri, uri2) - } - - @Test - @DisplayName("Test Serialize a remote UUri to micro without the address") - fun test_serialize_remote_uri_without_address() { - val uri: UUri = uUri { - authority = uAuthority { name = "vcu.vin" } - entity = uEntity { - id = 29999 - versionMajor = 254 - } - resource = uResource { id = 19999 } - } - - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(uri) - assertTrue(bytes.isEmpty()) - } - - @Test - @DisplayName("Test serialize Uri missing uE ID") - fun test_serialize_uri_missing_ids() { - val uri: UUri = uUri { - entity = uEntity { name = "hartley" } - resource = uResource { forRpcResponse() } - } - - - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(uri) - assertTrue(bytes.isEmpty()) - } - - @Test - @DisplayName("Test serialize Uri missing resource") - fun test_serialize_uri_missing_resource_id() { - val uri: UUri = uUri { entity = uEntity { name = "hartley" } } - - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(uri) - assertTrue(bytes.isEmpty()) - } - - @Test - @DisplayName("Test deserialize bad micro uri - length") - fun test_deserialize_bad_microuri_length() { - var badMicroUUri = byteArrayOf(0x1, 0x0, 0x0, 0x0, 0x0) - var uuri: UUri = MicroUriSerializer.INSTANCE.deserialize(badMicroUUri) - assertTrue(uuri.isEmpty()) - badMicroUUri = byteArrayOf(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) - uuri = MicroUriSerializer.INSTANCE.deserialize(badMicroUUri) - assertTrue(uuri.isEmpty()) - } - - @Test - @DisplayName("Test deserialize bad micro uri - not version 1") - fun test_deserialize_bad_microuri_not_version_1() { - val badMicroUUri = byteArrayOf(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) - val uuri: UUri = MicroUriSerializer.INSTANCE.deserialize(badMicroUUri) - assertTrue(uuri.isEmpty()) - } - - @Test - @DisplayName("Test deserialize bad micro uri - not valid address type") - fun test_deserialize_bad_microuri_not_valid_address_type() { - val badMicroUUri = byteArrayOf(0x1, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) - val uuri: UUri = MicroUriSerializer.INSTANCE.deserialize(badMicroUUri) - assertTrue(uuri.isEmpty()) - } - - @Test - @DisplayName("Test deserialize bad micro uri - valid address type and invalid length") - fun test_deserialize_bad_microuri_valid_address_type_invalid_length() { - var badMicroUUri = byteArrayOf(0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) - var uuri: UUri = MicroUriSerializer.INSTANCE.deserialize(badMicroUUri) - assertTrue(uuri.isEmpty()) - badMicroUUri = byteArrayOf(0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) - uuri = MicroUriSerializer.INSTANCE.deserialize(badMicroUUri) - assertTrue(uuri.isEmpty()) - badMicroUUri = byteArrayOf(0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) - uuri = MicroUriSerializer.INSTANCE.deserialize(badMicroUUri) - assertTrue(uuri.isEmpty()) - } - - @Test - @DisplayName("Test serialize with good IPv4 based authority") - @Throws(UnknownHostException::class) - fun test_serialize_good_ipv4_based_authority() { - val uri: UUri = uUri { - authority = uAuthority { ip = ByteString.copyFrom(InetAddress.getByName("10.0.3.3").address) } - entity = uEntity { - id = 29999 - versionMajor = 254 - } - resource = uResource { forRpcRequest( id = 99) } - } - - - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(uri) - val uri2: UUri = MicroUriSerializer.INSTANCE.deserialize(bytes) - assertTrue(bytes.isNotEmpty()) - assertTrue(uri.isMicroForm()) - assertTrue(uri2.isMicroForm()) - assertEquals(uri.toString(), uri2.toString()) - assertTrue(uri == uri2) - } - - @Test - @DisplayName("Test serialize with good IPv4 based authority and the uEntity major version is missing") - fun test_serialize_good_ipv4_based_authority_missing_major_version() { - val uri: UUri = uUri { - authority = uAuthority { ip = ByteString.copyFrom(toBytes("192.168.1.100")) } - entity = uEntity { id = 29999 } - resource = uResource { forRpcResponse() } - } - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(uri) - val uri2: UUri = MicroUriSerializer.INSTANCE.deserialize(bytes) - assertEquals(uri2.entity.versionMajor, 0) - } - - @Test - @DisplayName("Test serialize without uauthority ip or id") - fun test_serialize_without_uauthority_ip_or_id() { - val uri: UUri = uUri { - authority = uAuthority { name = "vcu.vin" } - entity = uEntity { - id = 29999 - versionMajor = 254 - } - resource = uResource { id = 19999 } - } - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(uri) - assertTrue(bytes.isEmpty()) - } - - @Test - @DisplayName("Test serialize with id that is out of range") - fun test_serialize_id_out_of_range() { - val byteArray = ByteArray(258) - for (i in 0..255) { - byteArray[i] = i.toByte() - } - val uri: UUri = uUri { - authority = uAuthority { ip = ByteString.copyFrom(byteArray) } - entity = uEntity { - id = 29999 - versionMajor = 254 - } - resource = uResource { from(19999) } - } - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(uri) - assertTrue(bytes.isEmpty()) - } - - @Test - @DisplayName("Test serialize with good IPv6 based authority") - @Throws(UnknownHostException::class) - fun test_serialize_good_ipv6_based_authority() { - val uri: UUri = uUri { - authority = uAuthority { - ip = ByteString.copyFrom( - InetAddress.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334").address - ) - } - entity = uEntity { - id = 29999 - versionMajor = 254 - } - - resource = uResource { from(19999) } - } - - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(uri) - val uri2: UUri = MicroUriSerializer.INSTANCE.deserialize(bytes) - assertTrue(uri.isMicroForm()) - assertTrue(bytes.isNotEmpty()) - assertTrue(uri == uri2) - } - - @Test - @DisplayName("Test serialize with ID based authority") - fun test_serialize_id_based_authority() { - val size = 13 - val byteArray = ByteArray(size) - // Assign values to the elements of the byte array - for (i in 0 until size) { - byteArray[i] = i.toByte() - } - val uri: UUri = uUri { - authority = uAuthority { id = ByteString.copyFrom(byteArray) } - entity = uEntity { - id = 29999 - versionMajor = 254 - } - resource = uResource { from(19999) } - } - - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(uri) - val uri2: UUri = MicroUriSerializer.INSTANCE.deserialize(bytes) - assertTrue(uri.isMicroForm()) - assertTrue(bytes.isNotEmpty()) - assertTrue(uri == uri2) - } - - @Test - @DisplayName("Test serialize with bad length IP based authority") - @Throws(UnknownHostException::class) - fun test_serialize_bad_length_ip_based_authority() { - val byteArray = byteArrayOf(127, 1, 23, 123, 12, 6) - val uri: UUri = uUri { - authority = uAuthority { - ip = ByteString.copyFrom(byteArray) - } - entity = uEntity { - id = 29999 - versionMajor = 254 - } - resource = uResource { from(19999) } - } - - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(uri) - assertTrue(bytes.isEmpty()) - } - - @Test - @DisplayName("Test serialize with ID based authority") - fun test_serialize_id_size_255_based_authority() { - val size = 129 - val byteArray = ByteArray(size) - // Assign values to the elements of the byte array - for (i in 0 until size) { - byteArray[i] = i.toByte() - } - val uri: UUri = uUri { - authority = uAuthority { id = ByteString.copyFrom(byteArray) } - entity = uEntity { - id = 29999 - versionMajor = 254 - } - resource = uResource { from(19999) } - } - - val bytes: ByteArray = MicroUriSerializer.INSTANCE.serialize(uri) - assertEquals(bytes.size, 9 + size) - val uri2: UUri = MicroUriSerializer.INSTANCE.deserialize(bytes) - assertTrue(uri.isMicroForm()) - assertTrue(uri == uri2) - } -} diff --git a/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/ShortUriSerializerTest.kt b/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/ShortUriSerializerTest.kt deleted file mode 100644 index 4dcaa1f..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/ShortUriSerializerTest.kt +++ /dev/null @@ -1,330 +0,0 @@ -/* -* Copyright (c) 2024 General Motors GTO LLC -* -* Licensed to the Apache Software Foundation (ASF) under one -* or more contributor license agreements. See the NOTICE file -* distributed with this work for additional information -* regarding copyright ownership. The ASF licenses this file -* to you 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-FileType: SOURCE -* SPDX-FileCopyrightText: 2024 General Motors GTO LLC -* SPDX-License-Identifier: Apache-2.0 -*/ - -package org.eclipse.uprotocol.uri.serializer - -import com.google.protobuf.ByteString -import org.eclipse.uprotocol.uri.serializer.IpAddress.toBytes -import org.eclipse.uprotocol.v1.* -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test - - -class ShortUriSerializerTest { - @Test - @DisplayName("Test serialize with empty uri") - fun testSerializeWithEmptyUri() { - val strUri: String = ShortUriSerializer.INSTANCE.serialize(UUri.getDefaultInstance()) - assertEquals("", strUri) - } - - - @Test - @DisplayName("Test Creating and using the ShortUriSerializer") - fun testCreatingShortUriSerializer() { - val uri: UUri = uUri { - entity = uEntity{ id = 1; versionMajor = 1 } - resource = uResource{ forRpcResponse() } - } - val strUri: String = ShortUriSerializer.INSTANCE.serialize(uri) - assertEquals("/1/1/0", strUri) - val uri2: UUri = ShortUriSerializer.INSTANCE.deserialize(strUri) - assertEquals(uri, uri2) - } - - @Test - @DisplayName("Test Creating and using the ShortUriSerializer with a method") - fun testCreatingShortUriSerializerWithMethod() { - val uri: UUri = uUri { - entity = uEntity{ id = 1; versionMajor = 1 } - resource = uResource { forRpcRequest(id = 10) } - } - - val strUri: String = ShortUriSerializer.INSTANCE.serialize(uri) - assertEquals("/1/1/10", strUri) - val uri2: UUri = ShortUriSerializer.INSTANCE.deserialize(strUri) - assertEquals(uri, uri2) - } - - @Test - @DisplayName("Test Creating and using the ShortUriSerializer with a topic") - fun testCreatingShortUriSerializerWithTopic() { - val uri: UUri = uUri { - entity = uEntity{ id = 1; versionMajor = 1 } - resource = uResource { from(20000) } - } - val strUri: String = ShortUriSerializer.INSTANCE.serialize(uri) - assertEquals("/1/1/20000", strUri) - val uri2: UUri = ShortUriSerializer.INSTANCE.deserialize(strUri) - assertEquals(uri, uri2) - } - - @Test - @DisplayName("Test Creating and using the ShortUriSerializer with id authority") - fun testCreatingShortUriSerializerWithAuthority() { - val uri: UUri = uUri { - entity = uEntity{ id = 1; versionMajor = 1 } - authority = uAuthority { id = ByteString.copyFromUtf8("19UYA31581L000000")} - resource = uResource { from(20000) } - } - - val strUri: String = ShortUriSerializer.INSTANCE.serialize(uri) - assertEquals("//19UYA31581L000000/1/1/20000", strUri) - val uri2: UUri = ShortUriSerializer.INSTANCE.deserialize(strUri) - assertEquals(uri, uri2) - } - - @Test - @DisplayName("Test Creating and using the ShortUriSerializer with ip authority") - fun testCreatingShortUriSerializerWithIpAuthority() { - val uri: UUri = uUri { - entity = uEntity{ id = 1; versionMajor = 1 } - authority = uAuthority { ip = ByteString.copyFrom(toBytes("192.168.1.100")) } - resource = uResource { from(20000) } - } - - val strUri: String = ShortUriSerializer.INSTANCE.serialize(uri) - assertEquals("//192.168.1.100/1/1/20000", strUri) - val uri2: UUri = ShortUriSerializer.INSTANCE.deserialize(strUri) - assertEquals(uri, uri2) - } - - @Test - @DisplayName("Test short serializing a URI that doesn't have a resource") - fun testShortSerializingUriWithoutResource() { - val uri = uUri { - entity = uEntity { id = 1; versionMajor = 1 } - } - val strUri: String = ShortUriSerializer.INSTANCE.serialize(uri) - assertEquals(strUri, "/1/1") - } - - @Test - @DisplayName("Test short serializing a URI that have a negative number for uEntity version major") - fun testShortSerializingUriWithNegativeVersionMajor() { - val uri: UUri = uUri { - entity = uEntity { id = 1; versionMajor = -1 } - resource = uResource { from(20000) } - } - val strUri: String = ShortUriSerializer.INSTANCE.serialize(uri) - assertEquals(strUri, "/1//20000") - } - - @Test - @DisplayName("Test short deserialize an empty URI") - fun testShortDeserializeEmptyUri() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("") - assertEquals(uri, UUri.getDefaultInstance()) - } - - @Test - @DisplayName("Test short deserialize of a valid URI with scheme") - fun testShortDeserializeUriWithSchemeAndAuthority() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("up://mypc/1/1/1") - assertTrue(uri.hasAuthority()) - assertEquals(uri.authority.id, ByteString.copyFromUtf8("mypc")) - assertFalse(uri.authority.hasName()) - assertFalse(uri.authority.hasIp()) - assertTrue(uri.hasEntity()) - assertEquals(uri.entity.id, 1) - assertEquals(uri.entity.versionMajor, 1) - assertTrue(uri.hasResource()) - assertEquals(uri.resource.id, 1) - } - - @Test - @DisplayName("Test short deserialize of a valid URI without scheme") - fun testShortDeserializeUriWithoutScheme() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("//mypc/1/1/1") - assertTrue(uri.hasAuthority()) - assertEquals(uri.authority.id, ByteString.copyFromUtf8("mypc")) - assertFalse(uri.authority.hasName()) - assertFalse(uri.authority.hasIp()) - assertTrue(uri.hasEntity()) - assertEquals(uri.entity.id, 1) - assertEquals(uri.entity.versionMajor, 1) - assertTrue(uri.hasResource()) - assertEquals(uri.resource.id, 1) - } - - @Test - @DisplayName("Test short deserialize a uri that only contains //") - fun testShortDeserializeUriWithOnlyAuthority() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("//") - assertEquals(uri, UUri.getDefaultInstance()) - } - - @Test - @DisplayName("Test short deserialize a uri with scheme and only contains //") - fun testShortDeserializeUriWithSchemeAndOnlyAuthority() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("up://") - assertEquals(uri, UUri.getDefaultInstance()) - } - - @Test - @DisplayName("Test short serialize with UAuthority ip address that is invalid") - fun testShortSerializeWithInvalidIpAddress() { - val uri = UUri.newBuilder() - .setEntity(UEntity.newBuilder().setId(1).setVersionMajor(1)) - .setAuthority(UAuthority.newBuilder().setIp(ByteString.copyFromUtf8("34823748273"))) - .build() - val uriString: String = ShortUriSerializer.INSTANCE.serialize(uri) - assertEquals(uriString, "") - } - - @Test - @DisplayName("Test short serialize with UAuthority that only have name and not ip or id") - fun testShortSerializeWithAuthorityOnlyName() { - val uri = UUri.newBuilder() - .setEntity(UEntity.newBuilder().setId(1).setVersionMajor(1)) - .setAuthority(UAuthority.newBuilder().setName("mypc")) - .build() - val uriString: String = ShortUriSerializer.INSTANCE.serialize(uri) - assertEquals(uriString, "") - } - - @Test - @DisplayName("Test short deserialize of a local URI that has too many parts") - fun testShortDeserializeLocalUriWithTooManyParts() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("/1/1/1/1") - assertEquals(uri, UUri.getDefaultInstance()) - } - - @Test - @DisplayName("Test short deserialize of a local URI that only has 2 parts") - fun testShortDeserializeLocalUriWithOnlyTwoParts() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("/1/1") - assertTrue(uri.hasEntity()) - assertEquals(uri.entity.id, 1) - assertEquals(uri.entity.versionMajor, 1) - } - - @Test - @DisplayName("Test short deserialize of a local URI that has 2 parts") - fun testShortDeserializeLocalUriWithThreeParts() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("/1") - assertTrue(uri.hasEntity()) - assertEquals(uri.entity.id, 1) - assertFalse(uri.hasResource()) - } - - @Test - @DisplayName("Test short deserialize with a blank authority") - fun testShortDeserializeWithBlankAuthority() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("///1/1/1") - assertEquals(uri, UUri.getDefaultInstance()) - } - - @Test - @DisplayName("Test short deserialize with a remote authority that is an IP address and too many parts in the uri") - fun testShortDeserializeWithRemoteAuthorityIpAndTooManyParts() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("//192.168.1.100/1/1/1/1") - assertEquals(uri, UUri.getDefaultInstance()) - } - - @Test - @DisplayName("Test short deserialize with a remote authority that is an IP address and right number of parts") - fun testShortDeserializeWithRemoteAuthorityIpAndRightNumberOfParts() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("//192.168.1.100/1/1/1") - assertTrue(uri.hasAuthority()) - assertTrue(uri.authority.hasIp()) - assertTrue(uri.authority.ip == ByteString.copyFrom(toBytes("192.168.1.100"))) - assertTrue(uri.hasEntity()) - assertEquals(uri.entity.id, 1) - assertEquals(uri.entity.versionMajor, 1) - assertTrue(uri.hasResource()) - assertEquals(uri.resource.id, 1) - } - - - @Test - @DisplayName("Test short deserialize with a remote authority that is an IP address but missing resource") - fun testShortDeserializeWithRemoteAuthorityIpAddressMissingResource() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("//192.168.1.100/1/1") - assertTrue(uri.hasAuthority()) - assertTrue(uri.authority.hasIp()) - assertTrue(uri.authority.ip == ByteString.copyFrom(toBytes("192.168.1.100"))) - assertTrue(uri.hasEntity()) - assertEquals(uri.entity.id, 1) - assertEquals(uri.entity.versionMajor, 1) - assertFalse(uri.hasResource()) - } - - @Test - @DisplayName("Test short deserialize with a remote authority that is an IP address but missing resource and major version") - fun testShortDeserializeWithRemoteAuthorityIpAddressMissingResourceAndVersionMajor() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("//192.168.1.100/1") - assertTrue(uri.hasAuthority()) - assertTrue(uri.authority.hasIp()) - assertTrue(uri.authority.ip == ByteString.copyFrom(toBytes("192.168.1.100"))) - assertTrue(uri.hasEntity()) - assertEquals(uri.entity.id, 1) - assertFalse(uri.entity.hasVersionMajor()) - } - - @Test - @DisplayName("Test short deserialize with a remote authority that is an IP address but missing resource and major version") - fun testShortDeserializeWithRemoteAuthorityIpAddressMissingResourceAndVersionMajorAndUeId() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("//192.168.1.100//") - assertTrue(uri.hasAuthority()) - assertTrue(uri.authority.hasIp()) - assertTrue(uri.authority.ip == ByteString.copyFrom(toBytes("192.168.1.100"))) - assertFalse(uri.hasEntity()) - } - - @Test - @DisplayName("Test short deserialize with a remote authority and blank ueversion and ueid") - fun testShortDeserializeWithRemoteAuthorityAndBlankUeVersionAndUeId() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("//mypc//1/") - assertTrue(uri.hasAuthority()) - assertTrue(uri.authority.hasId()) - assertEquals(uri.authority.id, ByteString.copyFromUtf8("mypc")) - assertTrue(uri.hasEntity()) - } - - @Test - @DisplayName("Test short deserialize with a remote authority and missing the other parts") - fun testShortDeserializeWithRemoteAuthorityAndMissingParts() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("//mypc") - assertTrue(uri.hasAuthority()) - assertTrue(uri.authority.hasId()) - assertEquals(uri.authority.id, ByteString.copyFromUtf8("mypc")) - } - - @Test - @DisplayName("Test short deserialize with a remote authority and invalid characters for entity id and version") - fun testShortDeserializeWithRemoteAuthorityAndInvalidEntityIdAndVersion() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("//mypc/abc/def") - assertEquals(uri, UUri.getDefaultInstance()) - } - - @Test - @DisplayName("Test short deserialize with a remote authority and invalid characters for resource id") - fun testShortDeserializeWithRemoteAuthorityAndInvalidResourceId() { - val uri: UUri = ShortUriSerializer.INSTANCE.deserialize("//mypc/1/1/abc") - assertEquals(uri.resource, UResource.getDefaultInstance()) - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/UriSerializerTest.kt b/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/UriSerializerTest.kt index 1768793..ed7699c 100644 --- a/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/UriSerializerTest.kt +++ b/src/test/kotlin/org/eclipse/uprotocol/uri/serializer/UriSerializerTest.kt @@ -1,189 +1,331 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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.uprotocol.uri.serializer -import com.google.protobuf.ByteString -import org.eclipse.uprotocol.UprotocolOptions -import org.eclipse.uprotocol.core.usubscription.v3.USubscriptionProto -import org.eclipse.uprotocol.uri.factory.UEntityFactory.fromProto import org.eclipse.uprotocol.uri.validator.isEmpty -import org.eclipse.uprotocol.uri.validator.isLongForm -import org.eclipse.uprotocol.uri.validator.isMicroForm -import org.eclipse.uprotocol.uri.validator.isShortForm -import org.eclipse.uprotocol.v1.* -import org.junit.jupiter.api.Assertions.* +import org.eclipse.uprotocol.v1.UUri +import org.eclipse.uprotocol.v1.uUri +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import java.net.InetAddress class UriSerializerTest { @Test - @DisplayName("Test building uSubscription Update Notification topic and comparing long, short, and micro URIs") - fun test_build_resolved_full_information_compare() { - val descriptor = USubscriptionProto.getDescriptor().services[0] + @DisplayName("Test using the serializers") + fun test_using_the_serializers() { + val uri = UUri.newBuilder() + .setAuthorityName("myAuthority") + .setUeId(1) + .setUeVersionMajor(2) + .setResourceId(3) + .build() - val entity = fromProto(descriptor) + val serializedUri: String = uri.serialize() + assertEquals("//myAuthority/1/2/3", serializedUri) + } + + @Test + @DisplayName("Test deserializing an empty UUri") + fun test_deserializing_an_empty_UUri() { + val uri: UUri = "".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + + @Test + @DisplayName("Test deserializing a blank UUri") + fun test_deserializing_a_blank_UUri() { + val uri: UUri = " ".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + @Test + @DisplayName("Test deserializing with a valid URI that has scheme") + fun test_deserializing_with_a_valid_URI_that_has_scheme() { + val uri: UUri = "up://myAuthority/1/2/3".deserializeAsUUri() + assertEquals("myAuthority", uri.authorityName) + assertEquals(1, uri.ueId) + assertEquals(2, uri.ueVersionMajor) + assertEquals(3, uri.resourceId) + } + + @Test + @DisplayName("Test deserializing with a valid URI that has scheme but nothing else") + fun test_deserializing_with_a_valid_URI_that_has_scheme_but_nothing_else() { + val uri: UUri = "up://".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + @Test + @DisplayName("Test deserializing a valid UUri with all fields") + fun test_deserializing_a_valid_UUri_with_all_fields() { + val uri: UUri = "//myAuthority/1/2/3".deserializeAsUUri() + assertEquals("myAuthority", uri.authorityName) + assertEquals(1, uri.ueId) + assertEquals(2, uri.ueVersionMajor) + assertEquals(3, uri.resourceId) + } + + @Test + @DisplayName("Test deserializing a valid UUri with only authority") + fun test_deserializing_a_valid_UUri_with_only_authority() { + val uri: UUri = "//myAuthority".deserializeAsUUri() + assertEquals("myAuthority", uri.authorityName) + assertEquals(0, uri.ueId) + assertEquals(0, uri.ueVersionMajor) + assertEquals(0, uri.resourceId) + } + + @Test + @DisplayName("Test deserializing a valid UUri with only authority and ueId") + fun test_deserializing_a_valid_UUri_with_only_authority_and_ueId() { + val uri: UUri = "//myAuthority/1".deserializeAsUUri() + assertEquals("myAuthority", uri.authorityName) + assertEquals(1, uri.ueId) + assertEquals(0, uri.ueVersionMajor) + assertEquals(0, uri.resourceId) + } + + @Test + @DisplayName("Test deserializing a valid UUri with only authority, ueId and ueVersionMajor") + fun test_deserializing_a_valid_UUri_with_only_authority_ueId_and_ueVersionMajor() { + val uri: UUri = "//myAuthority/1/2".deserializeAsUUri() + assertEquals("myAuthority", uri.authorityName) + assertEquals(1, uri.ueId) + assertEquals(2, uri.ueVersionMajor) + assertEquals(0, uri.resourceId) + } + + @Test + @DisplayName("Test deserializing a string with invalid characters at the beginning") + fun test_deserializing_a_string_with_invalid_characters() { + val uri: UUri = "$$".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + @Test + @DisplayName("Test deserializing a string with names instead of ids for UeId") + fun test_deserializing_a_string_with_names_instead_of_ids_for_UeId() { + val uri: UUri = "//myAuthority/myUeId/2/3".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + @Test + @DisplayName("Test deserializing a string with names instead of ids for UeVersionMajor") + fun test_deserializing_a_string_with_names_instead_of_ids_for_UeVersionMajor() { + val uri: UUri = "//myAuthority/1/myUeVersionMajor/3".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + @Test + @DisplayName("Test deserializing a string with names instead of ids for ResourceId") + fun test_deserializing_a_string_with_names_instead_of_ids_for_ResourceId() { + val uri: UUri = "//myAuthority/1/2/myResourceId".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + @Test + @DisplayName("Test deserializing a string without authority") + fun test_deserializing_a_string_without_authority() { + val uri: UUri = "/1/2/3".deserializeAsUUri() + assertEquals(1, uri.ueId) + assertEquals(2, uri.ueVersionMajor) + assertEquals(3, uri.resourceId) + assertTrue(uri.authorityName.isBlank()) + } + + @Test + @DisplayName("Test deserializing a string without authority and ResourceId") + fun test_deserializing_a_string_without_authority_and_ResourceId() { + val uri: UUri = "/1/2".deserializeAsUUri() + assertEquals(1, uri.ueId) + assertEquals(2, uri.ueVersionMajor) + assertEquals(0, uri.resourceId) + assertTrue(uri.authorityName.isBlank()) + } - val options = descriptor.options + @Test + @DisplayName("Test deserializing a string without authority, ResourceId and UeVersionMajor") + fun test_deserializing_a_string_without_authority_ResourceId_and_UeVersionMajor() { + val uri: UUri = "/1".deserializeAsUUri() + assertEquals(1, uri.ueId) + assertEquals(0, uri.ueVersionMajor) + assertEquals(0, uri.resourceId) + assertTrue(uri.authorityName.isBlank()) + } - val resource = options.getExtension(UprotocolOptions.notificationTopic).filter { topic -> - topic.name.contains("SubscriptionChange") - }.map { topic -> - uResource { - from(topic) - } - }.first() + @Test + @DisplayName("Test deserializing a string with blank authority") + fun test_deserializing_a_string_with_blank_authority() { + val uri: UUri = "///2".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } - val uUri = uUri { - this.entity = entity - this.resource = resource + @Test + @DisplayName("Test deserializing a string with all the items are the wildcard values") + fun test_deserializing_a_string_with_all_the_items_are_the_wildcard_values() { + val uri: UUri = "//*/FFFF/ff/ffff".deserializeAsUUri() + assertEquals("*", uri.authorityName) + assertEquals(0xFFFF, uri.ueId) + assertEquals(0xFF, uri.ueVersionMajor) + assertEquals(0xFFFF, uri.resourceId) + } + + @Test + @DisplayName("Test deserializing a string with uEId() out of range") + fun test_deserializing_a_string_with_uEId_out_of_range() { + val uri: UUri = "/fffffffff/2/3".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + @Test + @DisplayName("Test deserializing a string with uEVersionMajor out of range") + fun test_deserializing_a_string_with_uEVersionMajor_out_of_range() { + val uri: UUri = "/1/256/3".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + @Test + @DisplayName("Test deserializing a string with resourceId out of range") + fun test_deserializing_a_string_with_resourceId_out_of_range() { + val uri: UUri = "/1/2/65536".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + @Test + @DisplayName("Test deserializing a string with negative uEId") + fun test_deserializing_a_string_with_negative_uEId() { + val uri: UUri = "/-1/2/3".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + @Test + @DisplayName("Test deserializing a string with negative uEVersionMajor") + fun test_deserializing_a_string_with_negative_uEVersionMajor() { + val uri: UUri = "/1/-2/3".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + @Test + @DisplayName("Test deserializing a string with negative resourceId") + fun test_deserializing_a_string_with_negative_resourceId() { + val uri: UUri = "/1/2/-3".deserializeAsUUri() + assertTrue(uri.isEmpty()) + } + + @Test + @DisplayName("Test deserializing a string with wildcard ResourceId") + fun test_deserializing_a_string_with_wildcard_resourceId() { + val uri: UUri = "/1/2/ffff".deserializeAsUUri() + assertEquals(1, uri.ueId) + assertEquals(2, uri.ueVersionMajor) + assertEquals(0xFFFF, uri.resourceId) + } + + @Test + @DisplayName("Test serializing an Empty UUri") + fun test_serializing_an_empty_UUri() { + val uri = uUri { } + val serializedUri: String = uri.serialize() + assertTrue(serializedUri.isBlank()) + } + + @Test + @DisplayName("Test serializing a full UUri") + fun test_serializing_a_full_UUri() { + val uri = uUri { + authorityName = "myAuthority" + ueId = 1 + ueVersionMajor = 2 + resourceId = 3 } + val serializedUri: String = uri.serialize() + assertEquals("//myAuthority/1/2/3", serializedUri) + } + + @Test + @DisplayName("Test serializing a UUri with only authority") + fun test_serializing_a_UUri_with_only_authority() { + val uri = uUri { + authorityName = "myAuthority" - assertFalse(uUri.isEmpty()) - assertTrue(uUri.isMicroForm()) - assertTrue(uUri.isLongForm()) - assertTrue(uUri.isShortForm()) - val longUri: String = LongUriSerializer.INSTANCE.serialize(uUri) - val microUri: ByteArray = MicroUriSerializer.INSTANCE.serialize(uUri) - val shortUri: String = ShortUriSerializer.INSTANCE.serialize(uUri) - - assertEquals(longUri, "/core.usubscription/3/SubscriptionChange#Update") - assertEquals(shortUri, "/0/3/32768") - assertEquals(microUri.contentToString(), "[1, 0, -128, 0, 0, 0, 3, 0]") - } - - - @Test - @DisplayName("Test building uSubscription Update Notification topic with IPv4 address UAuthority and comparing long, short, and micro URIs") - fun test_build_resolved_full_information_compare_with_ipv4() { - val descriptor = USubscriptionProto.getDescriptor().services[0] - val entity = fromProto(descriptor) - val options = descriptor.options - val resource = options.getExtension(UprotocolOptions.notificationTopic).filter { topic -> - topic.name.contains("SubscriptionChange") - }.map { topic -> - uResource { - from(topic) - } - }.first() - - val uUri = uUri { - this.entity = entity - this.resource = resource - authority = uAuthority { - name = "vcu.veh.gm.com" - ip = ByteString.copyFrom(InetAddress.getByName("192.168.1.100").address) - } } + val serializedUri: String = uri.serialize() + assertEquals("//myAuthority/0/0/0", serializedUri) + } - assertFalse(uUri.isEmpty()) - assertTrue(uUri.isMicroForm()) - assertTrue(uUri.isLongForm()) - assertTrue(uUri.isShortForm()) - val longUri: String = LongUriSerializer.INSTANCE.serialize(uUri) - val microUri: ByteArray = MicroUriSerializer.INSTANCE.serialize(uUri) - val shortUri: String = ShortUriSerializer.INSTANCE.serialize(uUri) - - assertEquals(longUri, "//vcu.veh.gm.com/core.usubscription/3/SubscriptionChange#Update") - assertEquals(shortUri, "//192.168.1.100/0/3/32768") - assertEquals(microUri.contentToString(), "[1, 1, -128, 0, 0, 0, 3, 0, -64, -88, 1, 100]") - } - - @Test - @DisplayName("Test building uSubscription Update Notification topic with IPv6 address UAuthority and comparing long, short, and micro URIs") - fun test_build_resolved_full_information_compare_with_ipv6() { - val descriptor = USubscriptionProto.getDescriptor().services[0] - val entity = fromProto(descriptor) - val options = descriptor.options - val resource = options.getExtension(UprotocolOptions.notificationTopic).filter { topic -> - topic.name.contains("SubscriptionChange") - }.map { topic -> - uResource { - from(topic) - } - }.first() - - val uUri = uUri { - this.entity = entity - this.resource = resource - authority = uAuthority { - name = "vcu.veh.gm.com" - ip = ByteString.copyFrom(InetAddress.getByName("2001:db8:85a3:0:0:8a2e:370:7334").address) - } + @Test + @DisplayName("Test serializing a UUri with authority and ueId") + fun test_serializing_a_UUri_with_only_authority_and_ueId() { + val uri = uUri { + authorityName = "myAuthority" + ueId = 1 } + val serializedUri: String = uri.serialize() + assertEquals("//myAuthority/1/0/0", serializedUri) + } + + @Test + @DisplayName("Test serializing a UUri with authority, ueId and ueVersionMajor") + fun test_serializing_a_UUri_with_only_authority_ueId_and_ueVersionMajor() { + val uri = uUri { + authorityName = "myAuthority" + ueId = 1 + ueVersionMajor = 2 - assertFalse(uUri.isEmpty()) - assertTrue(uUri.isMicroForm()) - assertTrue(uUri.isLongForm()) - assertTrue(uUri.isShortForm()) - val longUri: String = LongUriSerializer.INSTANCE.serialize(uUri) - val microUri: ByteArray = MicroUriSerializer.INSTANCE.serialize(uUri) - val shortUri: String = ShortUriSerializer.INSTANCE.serialize(uUri) - - assertEquals(longUri, "//vcu.veh.gm.com/core.usubscription/3/SubscriptionChange#Update") - assertEquals(shortUri, "//2001:db8:85a3:0:0:8a2e:370:7334/0/3/32768") - assertEquals( - microUri.contentToString(), - "[1, 2, -128, 0, 0, 0, 3, 0, 32, 1, 13, -72, -123, -93, 0, 0, 0, 0, -118, 46, 3, 112, 115, 52]" - ) - } - - @Test - @DisplayName("Test building uSubscription Update Notification topic with id address UAuthority and comparing long, short, and micro URIs") - fun test_build_resolved_full_information_compare_with_id() { - val descriptor = USubscriptionProto.getDescriptor().services[0] - val entity = fromProto(descriptor) - val options = descriptor.options - - val resource = options.getExtension(UprotocolOptions.notificationTopic).filter { topic -> - topic.name.contains("SubscriptionChange") - }.map { topic -> - uResource { - from(topic) - } - }.first() - - val uUri = uUri { - this.entity = entity - this.resource = resource - authority = uAuthority { - name = "1G1YZ23J9P5800001.veh.gm.com" - id = ByteString.copyFromUtf8("1G1YZ23J9P5800001") - } } + val serializedUri: String = uri.serialize() + assertEquals("//myAuthority/1/2/0", serializedUri) + } + + @Test + @DisplayName("Test serializing a UUri with authority, ueId, ueVersionMajor and resourceId") + fun test_serializing_a_UUri_with_only_authority_ueId_ueVersionMajor_and_resourceId() { + val uri = uUri { + authorityName = "myAuthority" + ueId = 1 + ueVersionMajor = 2 + resourceId = 3 - assertFalse(uUri.isEmpty()) - assertTrue(uUri.isMicroForm()) - assertTrue(uUri.isLongForm()) - assertTrue(uUri.isShortForm()) - val longUri: String = LongUriSerializer.INSTANCE.serialize(uUri) - val microUri: ByteArray = MicroUriSerializer.INSTANCE.serialize(uUri) - val shortUri: String = ShortUriSerializer.INSTANCE.serialize(uUri) - - assertEquals(longUri, "//1G1YZ23J9P5800001.veh.gm.com/core.usubscription/3/SubscriptionChange#Update") - assertEquals(shortUri, "//1G1YZ23J9P5800001/0/3/32768") - assertEquals( - microUri.contentToString(), - "[1, 3, -128, 0, 0, 0, 3, 0, 17, 49, 71, 49, 89, 90, 50, 51, 74, 57, 80, 53, 56, 48, 48, 48, 48, 49]" - ) + } + val serializedUri: String = uri.serialize() + assertEquals("//myAuthority/1/2/3", serializedUri) + } + @Test + @DisplayName("Test serializing a UUri with empty authority, ueId, ueVersionMajor and resourceId") + fun test_serializing_a_UUri_with_empty_authority_ueId_ueVersionMajor_and_resourceId() { + val uri = uUri { + authorityName = "" + ueId = 1 + ueVersionMajor = 2 + resourceId = 3 + } + val serializedUri: String = uri.serialize() + assertEquals("/1/2/3", serializedUri) + } + + @Test + @DisplayName("Test serializing a UUri with blank authority, ueId, ueVersionMajor and resourceId") + fun test_serializing_a_UUri_with_blank_authority_ueId_ueVersionMajor_and_resourceId() { + val uri = uUri { + authorityName = " " + ueId = 1 + ueVersionMajor = 2 + resourceId = 3 + } + val serializedUri: String = uri.serialize() + assertEquals("/1/2/3", serializedUri) } } diff --git a/src/test/kotlin/org/eclipse/uprotocol/uri/validator/UriValidatorTest.kt b/src/test/kotlin/org/eclipse/uprotocol/uri/validator/UriValidatorTest.kt index 518cef2..4c5f497 100644 --- a/src/test/kotlin/org/eclipse/uprotocol/uri/validator/UriValidatorTest.kt +++ b/src/test/kotlin/org/eclipse/uprotocol/uri/validator/UriValidatorTest.kt @@ -1,881 +1,144 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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.uprotocol.uri.validator -import com.google.protobuf.ByteString -import org.eclipse.uprotocol.uri.serializer.LongUriSerializer import org.eclipse.uprotocol.v1.* -import org.eclipse.uprotocol.validation.ValidationResult -import org.json.JSONArray -import org.json.JSONObject import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import java.io.BufferedReader -import java.io.File -import java.io.FileReader -import java.io.IOException -@Suppress("KotlinConstantConditions") -internal class UriValidatorTest { +class UriValidatorTest { @Test - @DisplayName("Test validate uri with no device name") - fun test_validate_uri_with_no_entity_getName() { - val uri: UUri = LongUriSerializer.INSTANCE.deserialize("//") - val status: ValidationResult = uri.validate() - assertTrue(uri.isEmpty()) - assertEquals("Uri is empty.", status.getMessage()) - } - - @Test - @DisplayName("Test validate uri with uEntity") - fun test_validate_uri_with_getEntity() { - val uri: UUri = LongUriSerializer.INSTANCE.deserialize("/hartley") - val status: ValidationResult = uri.validate() - assertEquals(ValidationResult.success(), status) - } - - @Test - @DisplayName("Test validate with malformed URI") - fun test_validate_with_malformed_uri() { - val uri: UUri = LongUriSerializer.INSTANCE.deserialize("hartley") - val status: ValidationResult = uri.validate() - assertTrue(uri.isEmpty()) - assertEquals("Uri is empty.", status.getMessage()) - } - - @Test - @DisplayName("Test validate with blank UEntity Name") - fun test_validate_with_blank_uentity_name_uri() { - val status: ValidationResult = UUri.getDefaultInstance().validate() - assertTrue(status.isFailure()) - assertEquals("Uri is empty.", status.getMessage()) - } - - @Test - @DisplayName("Test validateRpcMethod with valid URI") - fun test_validateRpcMethod_with_valid_uri() { - val uri: UUri = LongUriSerializer.INSTANCE.deserialize("/hartley//rpc.echo") - val status: ValidationResult = uri.validateRpcMethod() - assertEquals(ValidationResult.success(), status) + @DisplayName("Test isEmpty with default UUri") + fun test_isEmpty_with_default_UUri() { + assertTrue(UUri.getDefaultInstance().isEmpty()) } @Test - @DisplayName("Test validateRpcMethod with invalid URI") - fun test_validateRpcMethod_with_invalid_uri() { - val uri: UUri = LongUriSerializer.INSTANCE.deserialize("/hartley/echo") - val status: ValidationResult = uri.validateRpcMethod() - assertTrue(status.isFailure()) - assertEquals("Uri is empty.", status.getMessage()) + @DisplayName("Test isEmpty for non empty UUri") + fun test_isEmpty_for_non_empty_UUri() { + val uri = UUri.newBuilder() + .setAuthorityName("myAuthority") + .setUeId(0) + .setUeVersionMajor(1) + .setResourceId(1).build() + assertFalse(uri.isEmpty()) + assertTrue(uri.isNotEmpty()) } @Test - @DisplayName("Test validateRpcMethod with malformed URI") - fun test_validateRpcMethod_with_malformed_uri() { - val uri: UUri = LongUriSerializer.INSTANCE.deserialize("hartley") - val status: ValidationResult = uri.validateRpcMethod() + @DisplayName("Test isEmpty UUri for empty built UUri") + fun test_isEmpty_UUri_for_empty_built_UUri() { + val uri = UUri.newBuilder().build() assertTrue(uri.isEmpty()) - assertEquals("Uri is empty.", status.getMessage()) - } - - @Test - @DisplayName("Test validateRpcResponse with valid URI") - fun test_validateRpcResponse_with_valid_uri() { - val uri: UUri = LongUriSerializer.INSTANCE.deserialize("/hartley//rpc.response") - val status: ValidationResult = uri.validateRpcResponse() - assertEquals(ValidationResult.success(), status) - } - - @Test - @DisplayName("Test validateRpcResponse with malformed URI") - fun test_validateRpcResponse_with_malformed_uri() { - val uri: UUri = LongUriSerializer.INSTANCE.deserialize("hartley") - val status: ValidationResult = uri.validateRpcResponse() - assertTrue(uri.isEmpty()) - assertEquals("Uri is empty.", status.getMessage()) - } - - @Test - @DisplayName("Test validateRpcResponse with rpc type") - fun test_validateRpcResponse_with_rpc_type() { - val uri: UUri = LongUriSerializer.INSTANCE.deserialize("/hartley//dummy.wrong") - val status: ValidationResult = uri.validateRpcResponse() - assertTrue(status.isFailure()) - assertEquals("Invalid RPC response type.", status.getMessage()) - } - - @Test - @DisplayName("Test validateRpcResponse with invalid rpc response type") - fun test_validateRpcResponse_with_invalid_rpc_response_type() { - val uri: UUri = LongUriSerializer.INSTANCE.deserialize("/hartley//rpc.wrong") - val status: ValidationResult = uri.validateRpcResponse() - assertTrue(status.isFailure()) - assertEquals("Invalid RPC response type.", status.getMessage()) - } - - @Test - @DisplayName("Test validate topic uri with version, when it is valid microRemote") - fun test_topic_uri_with_version_when_it_is_valid_remote() { - val uri = "//VCU.MY_CAR_VIN/body.access/1/door.front_left#Door" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validate() - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test validate topic uri no version, when it is valid microRemote") - fun test_topic_uri_no_version_when_it_is_valid_remote() { - val uri = "//VCU.MY_CAR_VIN/body.access//door.front_left#Door" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validate() - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test validate topic uri with version, when it is valid local") - fun test_topic_uri_with_version_when_it_is_valid_local() { - val uri = "/body.access/1/door.front_left#Door" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validate() - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test validate topic uri no version, when it is valid local") - fun test_topic_uri_no_version_when_it_is_valid_local() { - val uri = "/body.access//door.front_left#Door" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validate() - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test validate topic uri is invalid when uri contains nothing but schema") - fun test_topic_uri_invalid_when_uri_has_schema_only() { - val uri = ":" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validate() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate topic uri is invalid when uri contains empty use name local") - fun test_topic_uri_invalid_when_uri_has_empty_use_name_local() { - val uri = "/" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validate() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate topic uri is invalid when uri is microRemote but missing authority") - fun test_topic_uri_invalid_when_uri_is_remote_no_authority() { - val uri = "//" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validate() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate topic uri is invalid when uri is microRemote with use but missing authority") - fun test_topic_uri_invalid_when_uri_is_remote_no_authority_with_use() { - val uri = "///body.access/1/door.front_left#Door" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validate() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate topic uri is invalid when uri has no use information") - fun test_topic_uri_invalid_when_uri_is_missing_use_remote() { - val uri = "//VCU.myvin///door.front_left#Door" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validate() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate microRemote topic uri is invalid when uri is missing use name") - fun test_topic_uri_invalid_when_uri_is_missing_use_name_remote() { - val uri = "/1/door.front_left#Door" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validate() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate local topic uri is invalid when uri is missing use name") - fun test_topic_uri_invalid_when_uri_is_missing_use_name_local() { - val uri = "//VCU.myvin//1" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validate() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate rpc topic uri with version, when it is valid microRemote") - fun test_rpc_topic_uri_with_version_when_it_is_valid_remote() { - val uri = "//bo.cloud/petapp/1/rpc.response" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test validate rpc topic uri no version, when it is valid microRemote") - fun test_rpc_topic_uri_no_version_when_it_is_valid_remote() { - val uri = "//bo.cloud/petapp//rpc.response" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test validate rpc topic uri with version, when it is valid local") - fun test_rpc_topic_uri_with_version_when_it_is_valid_local() { - val uri = "/petapp/1/rpc.response" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test validate rpc topic uri no version, when it is valid local") - fun test_rpc_topic_uri_no_version_when_it_is_valid_local() { - val uri = "/petapp//rpc.response" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test validate rpc topic uri is invalid when uri contains nothing but schema") - fun test_rpc_topic_uri_invalid_when_uri_has_schema_only() { - val uri = ":" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate rpc topic uri with version, when it is local but missing rpc.respons") - fun test_rpc_topic_uri_with_version_when_it_is_not_valid_missing_rpc_response_local() { - val uri = "/petapp/1/dog" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) + assertFalse(uri.isNotEmpty()) } @Test - @DisplayName("Test validate rpc topic uri with version, when it is microRemote but missing rpc.respons") - fun test_rpc_topic_uri_with_version_when_it_is_not_valid_missing_rpc_response_remote() { - val uri = "//petapp/1/dog" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) + @DisplayName("Test isRpcMethod with default UUri") + fun test_isRpcMethod_with_default_UUri() { + assertFalse(UUri.getDefaultInstance().isRpcMethod()) } @Test - @DisplayName("Test validate rpc topic uri is invalid when uri is microRemote but missing authority") - fun test_rpc_topic_uri_invalid_when_uri_is_remote_no_authority() { - val uri = "//" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) + @DisplayName("Test isRpcMethod with UUri having resourceId less than MIN_TOPIC_ID") + fun test_isRpcMethod_with_UUri_having_resourceId_less_than_MIN_TOPIC_ID() { + val uri = UUri.newBuilder() + .setResourceId(0x7FFF).build() + assertTrue(uri.isRpcMethod()) } @Test - @DisplayName("Test validate rpc topic uri is invalid when uri is microRemote with use but missing authority") - fun test_rpc_topic_uri_invalid_when_uri_is_remote_no_authority_with_use() { - val uri = "///body.access/1" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) + @DisplayName("Test isRpcMethod with UUri having resourceId greater than MIN_TOPIC_ID") + fun test_isRpcMethod_with_UUri_having_resourceId_greater_than_MIN_TOPIC_ID() { + val uri = UUri.newBuilder() + .setResourceId(0x8000).build() + assertTrue(!uri.isRpcMethod()) } @Test - @DisplayName("Test validate rpc topic uri is invalid when uri has no use information") - fun test_rpc_topic_uri_invalid_when_uri_is_missing_use() { - val uri = "//VCU.myvin" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) + @DisplayName("Test isRpcMethod with UUri having resourceId equal to MIN_TOPIC_ID") + fun test_isRpcMethod_with_UUri_having_resourceId_equal_to_MIN_TOPIC_ID() { + val uri = UUri.newBuilder() + .setResourceId(0x8000).build() + assertTrue(!uri.isRpcMethod()) } @Test - @DisplayName("Test validate microRemote rpc topic uri is invalid when uri is missing use name") - fun test_rpc_topic_uri_invalid_when_uri_is_missing_use_name_remote() { - val uri = "/1" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) + @DisplayName("Test isRpcResponse with default UUri") + fun test_isRpcResponse_with_default_UUri() { + assertTrue(!UUri.getDefaultInstance().isRpcResponse()) } @Test - @DisplayName("Test validate local rpc topic uri is invalid when uri is missing use name") - fun test_rpc_topic_uri_invalid_when_uri_is_missing_use_name_local() { - val uri = "//VCU.myvin//1" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate rpc method uri with version, when it is valid microRemote") - fun test_rpc_method_uri_with_version_when_it_is_valid_remote() { - val uri = "//VCU.myvin/body.access/1/rpc.UpdateDoor" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test validate rpc method uri no version, when it is valid microRemote") - fun test_rpc_method_uri_no_version_when_it_is_valid_remote() { - val uri = "//VCU.myvin/body.access//rpc.UpdateDoor" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test validate rpc method uri with version, when it is valid local") - fun test_rpc_method_uri_with_version_when_it_is_valid_local() { - val uri = "/body.access/1/rpc.UpdateDoor" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test validate rpc method uri no version, when it is valid local") - fun test_rpc_method_uri_no_version_when_it_is_valid_local() { - val uri = "/body.access//rpc.UpdateDoor" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test validate rpc method uri is invalid when uri contains nothing but schema") - fun test_rpc_method_uri_invalid_when_uri_has_schema_only() { - val uri = ":" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate rpc method uri with version, when it is local but not an rpc method") - fun test_rpc_method_uri_with_version_when_it_is_not_valid_not_rpc_method_local() { - val uri = "/body.access//UpdateDoor" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate rpc method uri with version, when it is microRemote but not an rpc method") - fun test_rpc_method_uri_with_version_when_it_is_not_valid_not_rpc_method_remote() { - val uri = "//body.access/1/UpdateDoor" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate rpc method uri is invalid when uri is microRemote but missing authority") - fun test_rpc_method_uri_invalid_when_uri_is_remote_no_authority() { - val uri = "//" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate rpc method uri is invalid when uri is microRemote with use but missing authority") - fun test_rpc_method_uri_invalid_when_uri_is_remote_no_authority_with_use() { - val uri = "///body.access/1/rpc.UpdateDoor" - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uri) - val status: ValidationResult = uuri.validateRpcMethod() - assertEquals("", uuri.toString()) - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate rpc method uri is invalid when uri has authority but missing remote case") - fun test_rpc_method_uri_invalid_when_uri_is_remote_missing_authority_remotecase() { - val uuri: UUri = uUri { - entity = uEntity { name = "body.access" } - resource = uResource { - name = "rpc" - instance = "UpdateDoor" - } - authority = uAuthority { } - } - - - val status: ValidationResult = uuri.validateRpcMethod() - assertTrue(status.isFailure()) - assertEquals("Uri is remote missing uAuthority.", status.getMessage()) - } - - @Test - @DisplayName("Test validate rpc method uri is invalid when uri has no use information") - fun test_rpc_method_uri_invalid_when_uri_is_missing_use() { - val uri = "//VCU.myvin" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate local rpc method uri is invalid when uri is missing use name") - fun test_rpc_method_uri_invalid_when_uri_is_missing_use_name_local() { - val uri = "/1/rpc.UpdateDoor" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test validate microRemote rpc method uri is invalid when uri is missing use name") - fun test_rpc_method_uri_invalid_when_uri_is_missing_use_name_remote() { - val uri = "//VCU.myvin//1/rpc.UpdateDoor" - val status: ValidationResult = LongUriSerializer.INSTANCE.deserialize(uri).validateRpcMethod() - assertTrue(status.isFailure()) - } - - @Test - @DisplayName("Test all valid uris from uris.json") - @Throws(IOException::class) - fun test_all_valid_uris() { - // Access the "validUris" array - val validUris: JSONArray = jsonObject.getJSONArray("validUris") - for (i in 0 until validUris.length()) { - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(validUris.getString(i)) - val status: ValidationResult = uuri.validate() - assertTrue(status.isSuccess()) - } - } - - @Test - @DisplayName("Test all invalid uris from uris.json") - @Throws(IOException::class) - fun test_all_invalid_uris() { - // Access the "invalidUris" array - val invalidUris: JSONArray = jsonObject.getJSONArray("invalidUris") - for (i in 0 until invalidUris.length()) { - val uriObject: JSONObject = invalidUris.getJSONObject(i) - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uriObject.getString("uri")) - val status: ValidationResult = uuri.validate() - assertTrue(status.isFailure()) - assertEquals(status.getMessage(), uriObject.getString("status_message")) - } - } - - @Test - @DisplayName("Test all valid rpc uris from uris.json") - @Throws(IOException::class) - fun test_all_valid_rpc_uris() { - // Access the "validRpcUris" array - val validRpcUris: JSONArray = jsonObject.getJSONArray("validRpcUris") - for (i in 0 until validRpcUris.length()) { - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(validRpcUris.getString(i)) - val status: ValidationResult = uuri.validateRpcMethod() - assertTrue(status.isSuccess()) - } - } - - @Test - @DisplayName("Test all invalid rpc uris from uris.json") - @Throws(IOException::class) - fun test_all_invalid_rpc_uris() { - // Access the "invalidRpcUris" array - val invalidRpcUris: JSONArray = jsonObject.getJSONArray("invalidRpcUris") - for (i in 0 until invalidRpcUris.length()) { - val uriObject: JSONObject = invalidRpcUris.getJSONObject(i) - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(uriObject.getString("uri")) - val status: ValidationResult = uuri.validateRpcMethod() - assertTrue(status.isFailure()) - assertEquals(status.getMessage(), uriObject.getString("status_message")) - } - } - - @Test - @DisplayName("Test all valid rpc response uris from uris.json") - @Throws(IOException::class) - fun test_all_valid_rpc_response_uris() { - // Access the "validRpcResponseUris" array - val validRpcResponseUris: JSONArray = jsonObject.getJSONArray("validRpcResponseUris") - for (i in 0 until validRpcResponseUris.length()) { - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(validRpcResponseUris.getString(i)) - val status: ValidationResult = uuri.validateRpcResponse() - assertTrue(uuri.isRpcResponse()) - assertTrue(status.isSuccess()) - } - } - - @Test - @DisplayName("Test valid rpc response uri") - @Throws(IOException::class) - fun test_valid_rpc_response_uri() { - val uuri: UUri = uUri { - entity = uEntity { name = "hartley" } - resource = uResource { - forRpcResponse() - } - } - - val status: ValidationResult = uuri.validateRpcResponse() - assertTrue(uuri.isRpcResponse()) - assertTrue(status.isSuccess()) - } - - @Test - @DisplayName("Test invalid rpc method uri") - fun test_invalid_rpc_method_uri() { - val uuri = uUri { - entity = uEntity { - name = "hello.world" - } - resource = uResource { - name = "testrpc" - instance = "SayHello" - } - } - val status: ValidationResult = uuri.validateRpcMethod() - assertFalse(uuri.isRpcMethod()) - assertFalse(status.isSuccess()) - } - - @Test - @DisplayName("Test invalid rpc response uri") - @Throws(IOException::class) - fun test_invalid_rpc_response_uri() { - val uuri1: UUri = uUri { - entity = uEntity { name = "hartley" } - resource = uResource { - name = "rpc" - id = 19999 - } - } - val status1 = uuri1.validateRpcResponse() - assertFalse(uuri1.isRpcResponse()) - assertFalse(status1.isSuccess()) - - val uuri2: UUri = uUri { - entity = uEntity { name = "hartley" } - resource = uResource { - name = "testrpc" - instance = "response" - } - } - val status2 = uuri2.validateRpcResponse() - assertFalse(uuri2.isRpcResponse()) - assertFalse(status2.isSuccess()) - - val uuri3: UUri = uUri { - entity = uEntity { name = "hartley" } - resource = uResource { - name = "rpc" - instance = "testresponse" - } - } - val status3 = uuri3.validateRpcResponse() - assertFalse(uuri3.isRpcResponse()) - assertFalse(status3.isSuccess()) - } - - @Test - @DisplayName("Test invalid rpc response uri") - @Throws(IOException::class) - fun test_another_invalid_rpc_response_uri() { - val uuri: UUri = uUri { - entity = uEntity { name = "hartley" } - resource = uResource { - name = "hello" - id = 19999 - } - } - - val status = uuri.validateRpcResponse() - assertFalse(uuri.isRpcResponse()) - assertFalse(status.isSuccess()) - } - - @Test - @DisplayName("Test all invalid rpc response uris from uris.json") - @Throws(IOException::class) - fun test_all_invalid_rpc_response_uris() { - // Access the "invalidRpcResponseUris" array - val invalidRpcResponseUris: JSONArray = jsonObject.getJSONArray("invalidRpcResponseUris") - for (i in 0 until invalidRpcResponseUris.length()) { - val uuri: UUri = LongUriSerializer.INSTANCE.deserialize(invalidRpcResponseUris.getString(i)) - val status: ValidationResult = uuri.validateRpcResponse() - assertTrue(status.isFailure()) - } - } - - @Test - @DisplayName("Test is Remote is false for URI without UAuthority") - fun test_is_remote_is_false_for_uri_without_uauthority() { - val uri = uUri { - authority = uAuthority { } - entity = uEntity { name = "hartley" } - resource = uResource { - forRpcResponse() - } - } - assertFalse(UAuthority.getDefaultInstance().isRemote()) - assertFalse(uri.authority.isRemote()) - } - - @Test - @DisplayName("Test isRpcMethod with UResource and no UAuthority") - fun test_is_rpc_method_with_uresource_and_no_uauthority() { - assertFalse(uUri { }.isRpcMethod()) - - val uri: UUri = uUri { - entity = uEntity { name = "hartley" } - resource = uResource { from(0x8000) } - } - assertFalse(uri.isRpcMethod()) - } - - @Test - @DisplayName("Test isRpcMethod for UResource without an instance") - fun test_is_rpc_method_for_uresource_without_an_instance() { - val resource = uResource { name = "rpc" } - assertFalse(resource.isRpcMethod()) - } - - @Test - @DisplayName("Test isRpcMethod for UResource an empty instance") - fun test_is_rpc_method_for_uresource_with_an_empty_instance() { - val resource = uResource { - name = "rpc" - instance = "" - } - assertFalse(resource.isRpcMethod()) - } - - @Test - @DisplayName("Test isRpcMethod for UResource with id that is less than min_topic") - fun test_is_rpc_method_for_uresource_with_id_that_is_less_than_min_topic() { - val resource = uResource { - name = "rpc" - id = 0 - } - assertTrue(resource.isRpcMethod()) - } - - @Test - @DisplayName("Test isRpcMethod for UResource with id that is greater than min_topic") - fun test_is_rpc_method_for_uresource_with_id_that_is_greater_than_min_topic() { - val resource = uResource { - name = "rpc" - id = 0x8000 - } - assertFalse(resource.isRpcMethod()) - } - - @Test - @DisplayName("Test isResolved when URI is long form only") - fun test_is_resolved_when_uri_is_long_form_only() { - val uri = uUri { - entity = uEntity { - name = "hartley" - versionMajor = 23 - } - resource = uResource { - name = "rpc" - instance = "echo" - } - } - assertFalse(uri.isResolved()) - } - - @Test - @DisplayName("Test isResolved when URI is micro form only") - fun test_is_resolved_when_uri_is_micro_form_only() { - val uri: UUri = uUri { - entity = uEntity { - id = 0 - versionMajor = 23 - } - resource = uResource { from(0x8000) } - } - assertFalse(uri.isResolved()) - } - - @Test - @DisplayName("Test isResolved when URI is both long and micro form") - fun test_is_resolved_when_uri_is_both_long_and_micro_form() { - val uri: UUri = uUri { - entity = uEntity { - name = "hartley" - id = 0 - versionMajor = 23 - } - resource = uResource { forRpcResponse() } - } - assertTrue(uri.isResolved()) - } - - @Test - @DisplayName("Test isRpcResponse when uri is a valid RPC response") - fun test_is_rpc_response_when_uri_is_a_valid_rpc_response() { - val uri: UUri = uUri { - entity = uEntity { - name = "hartley" - id = 0 - versionMajor = 23 - } - resource = uResource { forRpcResponse() } - } + @DisplayName("Test isRpcResponse with UUri having resourceId equal to 0") + fun test_isRpcResponse_with_UUri_having_resourceId_equal_to_0() { + val uri = UUri.newBuilder() + .setAuthorityName("hartley") + .setUeId(1) + .setUeVersionMajor(1) + .setResourceId(0).build() assertTrue(uri.isRpcResponse()) } @Test - @DisplayName("Test isMicroForm when URI is empty") - fun test_is_micro_form_when_uri_is_empty() { - assertFalse(UUri.getDefaultInstance().isMicroForm()) - } - - @Test - @DisplayName("Test isMicroForm when URI does not have UResource but does have UEntity") - fun test_is_micro_form_when_uri_does_not_have_uresource_but_does_have_uentity() { - val uri = uUri { - entity = uEntity { - name = "hartley" - id = 0 - versionMajor = 23 - } - } - - assertFalse(uri.isMicroForm()) - } - - @Test - @DisplayName("Test isLongForm when URI is null") - fun test_is_long_form_when_uri_is_null() { - assertFalse((null as UAuthority?).isLongForm()) - } - - @Test - @DisplayName("Test isLongForm when UAuthority is not long form") - fun test_is_long_form_when_uauthority_is_not_long_form() { - val uri = uUri { - entity = uEntity { - name = "hartley" - id = 0 - versionMajor = 23 - } - authority = UAuthority.getDefaultInstance() - } - assertFalse(uri.isLongForm()) - assertTrue(uri.authority.isLongForm()) + @DisplayName("Test isRpcResponse with UUri having resourceId not equal to 0") + fun test_isRpcResponse_with_UUri_having_resourceId_not_equal_to_0() { + val uri = UUri.newBuilder() + .setAuthorityName("hartley") + .setUeId(1) + .setUeVersionMajor(1) + .setResourceId(1).build() + assertTrue(!uri.isRpcResponse()) } @Test - @DisplayName("Test isLongForm when UAuthority is long form but not the rest") - fun test_is_long_form_when_uauthority_is_long_form_but_not_the_rest() { - val uri = uUri { - entity = uEntity { - name = "hartley" - id = 0 - versionMajor = 23 - } - authority = uAuthority { name = "vcu.veh.gm.com" } - } - assertFalse(uri.isLongForm()) - assertTrue(uri.authority.isLongForm()) + @DisplayName("Test isRpcResponse with UUri having resourceId less than 0") + fun test_isRpcResponse_with_UUri_having_resourceId_less_than_0() { + val uri = UUri.newBuilder() + .setResourceId(-1).build() + assertTrue(!uri.isRpcResponse()) } @Test - @DisplayName("Test isLongForm when UAuthority blank name") - fun test_is_long_form_when_uauthority_is_blank_name() { - val uri = uUri { - entity = uEntity { - name = "hartley" - id = 0 - versionMajor = 23 - } - authority = uAuthority { name = "" } - } - assertFalse(uri.isLongForm()) - assertFalse(uri.authority.isLongForm()) + @DisplayName("Test isTopic with default UUri") + fun test_isTopic_with_default_UUri() { + assertFalse(UUri.getDefaultInstance().isTopic()) } @Test - @DisplayName("Test isLongForm when UAuthority is not long form but the rest is") - fun test_is_long_form_when_uauthority_is_not_long_form_but_the_rest_is() { - val uri = uUri { - entity = uEntity { - name = "hartley" - id = 0 - versionMajor = 23 - } - resource = uResource { forRpcResponse() } - authority = uAuthority { id = ByteString.copyFromUtf8("hello Jello") } - } - assertFalse(uri.isLongForm()) - assertFalse(uri.authority.isLongForm()) + @DisplayName("Test isTopic with UUri having resourceId greater than 0") + fun test_isTopic_with_UUri_having_resourceId_greater_than_0() { + val uri = UUri.newBuilder() + .setResourceId(1).build() + assertFalse(uri.isTopic()) } @Test - @DisplayName("Test isLocal when authority is null") - fun test_is_local_when_authority_is_null() { - assertFalse((null as UAuthority?).isLocal()) + @DisplayName("Test isTopic with UUri having resourceId greater than 0x8000") + fun test_isTopic_with_UUri_having_resourceId_greater_than_0x8000() { + val uri = UUri.newBuilder() + .setResourceId(0x8001).build() + assertTrue(uri.isTopic()) } @Test - @DisplayName("Test isRemote when authority is null") - fun test_is_remote_when_authority_is_null() { - assertFalse((null as UAuthority?).isRemote()) - } - - @Test - @DisplayName("Test isRemote when authority doesn't have a name but does have a number set") - fun test_is_remote_when_authority_does_not_have_a_name_but_does_have_a_number_set() { - val authority = uAuthority { - id = ByteString.copyFromUtf8("hello Jello") - } - assertTrue(authority.isRemote()) - assertFalse(authority.hasName()) - assertEquals(authority.numberCase, UAuthority.NumberCase.ID) - } - - @Test - @DisplayName("Test isRemote when authority has name and number set") - fun test_is_remote_when_authority_has_name_and_number_set() { - val authority = uAuthority { - name = "vcu.veh.gm.com" - id = ByteString.copyFromUtf8("hello Jello") - } - assertTrue(authority.isRemote()) - assertTrue(authority.hasName()) - assertEquals(authority.numberCase, UAuthority.NumberCase.ID) - } - - @Test - @DisplayName("Test isRemote when authority has name and number is NOT set") - fun test_is_remote_when_authority_has_name_and_number_is_not_set() { - val authority = uAuthority { - name = "vcu.veh.gm.com" - } - assertTrue(authority.isRemote()) - assertTrue(authority.hasName()) - assertEquals(authority.numberCase, UAuthority.NumberCase.NUMBER_NOT_SET) + @DisplayName("Test isRpcMethod should be false when resourceId is 0") + fun test_isRpcMethod_should_be_false_when_resourceId_is_0() { + val uri = UUri.newBuilder() + .setUeId(1) + .setResourceId(0).build() + assertFalse(uri.isRpcMethod()) } - - @get:Throws(IOException::class) - private val jsonObject: JSONObject - get() { - val currentDirectory: String = System.getProperty("user.dir") - val pkgname: String = this.javaClass.getPackage().name.replace(".", "/") - val jsonFile = File( - currentDirectory, - ((("src" + File.separator) + "test" + File.separator) + "kotlin" + File.separator + pkgname + File.separator) + "uris.json" - ) - - // Open the file for reading - val reader = BufferedReader(FileReader(jsonFile)) - // Read the JSON data as a string - val jsonStringBuilder = StringBuilder() - var line: String? - while (reader.readLine().also { line = it } != null) { - jsonStringBuilder.append(line) - } - reader.close() - // Parse the JSON data into a JSONObject - return JSONObject(jsonStringBuilder.toString()) - } } diff --git a/src/test/kotlin/org/eclipse/uprotocol/uri/validator/uris.json b/src/test/kotlin/org/eclipse/uprotocol/uri/validator/uris.json deleted file mode 100644 index 2865428..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/uri/validator/uris.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "validUris": [ - "/hartley", - "/hartley//", - "hartley/0", - "/1", - "/body.access/1", - "/body.access/1/door.front_left#Door", - "//vcu.vin/body.access/1/door.front_left#Door", - "/body.access/1/rpc.OpenWindow", - "/body.access/1/rpc.response" - ], - "invalidUris": [ - { - "uri": "/", - "status_message": "Uri is empty.", - "reason": "Empty Uri" - }, - { - "uri": "//", - "status_message": "Uri is empty.", - "reason": "Empty Uri" - }, - { - "uri": "//vcu", - "status_message": "Uri is missing uSoftware Entity name.", - "reason": "Missing entity name." - }, - { - "uri": "//vcu.vin/", - "status_message": "Uri is missing uSoftware Entity name.", - "reason": "Missing entity name." - }, - { - "uri": "", - "status_message": "Uri is empty.", - "reason": "Empty Uri" - }, - { - "uri": ":", - "status_message": "Uri is empty.", - "reason": "Contains only schema" - }, - { - "uri": "///", - "status_message": "Uri is empty.", - "reason": "Empty Authority" - }, - { - "uri": "////", - "status_message": "Uri is empty.", - "reason": "Empty Uri" - }, - { - "uri": "1", - "status_message": "Uri is empty.", - "reason": "Invalid Uri, must begin with \"\/\"" - }, - { - "uri": "a", - "status_message": "Uri is empty.", - "reason": "Invalid Uri, must begin with \"\/\"" - } - ], - "validRpcUris": [ - "/petapp/1/rpc.OpenWindow", - "/petapp/1/rpc.response" - ], - "validRpcResponseUris": [ - "/petapp/1/rpc.response" - ], - "invalidRpcResponseUris": [ - "/petapp/1/rpc.OpenWindow" - ], - "invalidRpcUris": [ - { - "uri": "/petapp//", - "reason": "Missing uE version", - "status_message": "Invalid RPC method uri. Uri should be the method to be called, or method from response." - }, - { - "uri": "/petapp", - "reason": "Missing uE version", - "status_message": "Invalid RPC method uri. Uri should be the method to be called, or method from response." - }, - { - "uri": "/petapp/1/", - "reason": "Missing RPC Method Name", - "status_message": "Invalid RPC method uri. Uri should be the method to be called, or method from response." - }, - { - "uri": "/petapp/1/rpc", - "reason": "Missing RPC Method Name", - "status_message": "Invalid RPC method uri. Uri should be the method to be called, or method from response." - }, - { - "uri": "/petapp/1/dummy", - "reason": "Missing RPC Method Name", - "status_message": "Invalid RPC method uri. Uri should be the method to be called, or method from response." - }, - { - "uri": "/petapp/1/rpc_dummy", - "reason": "Missing RPC Method Name", - "status_message": "Invalid RPC method uri. Uri should be the method to be called, or method from response." - } - ] -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/uuid/factory/UUIDFactoryTest.kt b/src/test/kotlin/org/eclipse/uprotocol/uuid/factory/UUIDFactoryTest.kt index a3cf7b5..348bd00 100644 --- a/src/test/kotlin/org/eclipse/uprotocol/uuid/factory/UUIDFactoryTest.kt +++ b/src/test/kotlin/org/eclipse/uprotocol/uuid/factory/UUIDFactoryTest.kt @@ -1,28 +1,20 @@ -/* - * Copyright (c) 2023 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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.uprotocol.uuid.factory -import org.eclipse.uprotocol.uuid.serializer.LongUuidSerializer -import org.eclipse.uprotocol.uuid.serializer.MicroUuidSerializer +import org.eclipse.uprotocol.uuid.serializer.deserializeAsUUID +import org.eclipse.uprotocol.uuid.serializer.serialize import org.eclipse.uprotocol.v1.UUID import org.eclipse.uprotocol.v1.uUID import org.junit.jupiter.api.Assertions.* @@ -39,10 +31,9 @@ class UUIDFactoryTest { fun test_uuidv8_creation() { val now: Instant = Instant.now() val uuid: UUID = UUIDV8(now) - val version= uuid.getVersion() + val version = uuid.getVersion() val time: Long? = uuid.getTime() - val bytes = MicroUuidSerializer.INSTANCE.serialize(uuid) - val uuidString = LongUuidSerializer.INSTANCE.serialize(uuid) + val uuidString = uuid.serialize() assertNotNull(uuid) assertTrue(uuid.isUProtocol()) @@ -51,14 +42,11 @@ class UUIDFactoryTest { assertNotNull(version) assertNotNull(time) assertEquals(now.toEpochMilli(), time) - assertTrue(bytes.isNotEmpty()) assertFalse(uuidString.isBlank()) - val uuid1: UUID = MicroUuidSerializer.INSTANCE.deserialize(bytes) - val uuid2: UUID = LongUuidSerializer.INSTANCE.deserialize(uuidString) + + val uuid1: UUID = uuidString.deserializeAsUUID() assertFalse(uuid1 == UUID.getDefaultInstance()) - assertFalse(uuid2 == UUID.getDefaultInstance()) assertEquals(uuid, uuid1) - assertEquals(uuid, uuid2) } @Test @@ -91,8 +79,7 @@ class UUIDFactoryTest { val uuid: UUID = UUIDV6(now) val version = uuid.getVersion() val time: Long? = uuid.getTime() - val bytes = MicroUuidSerializer.INSTANCE.serialize(uuid) - val uuidString = LongUuidSerializer.INSTANCE.serialize(uuid) + val uuidString = uuid.serialize() assertNotNull(uuid) assertTrue(uuid.isUuidv6()) assertTrue(uuid.isUuid()) @@ -100,13 +87,10 @@ class UUIDFactoryTest { assertNotNull(version) assertNotNull(time) assertEquals(now.toEpochMilli(), time) - assertTrue(bytes.isNotEmpty()) + assertFalse(uuidString.isBlank()) - val uuid1: UUID = MicroUuidSerializer.INSTANCE.deserialize(bytes) - val uuid2: UUID = LongUuidSerializer.INSTANCE.deserialize(uuidString) - assertFalse(uuid1 == UUID.getDefaultInstance()) + val uuid2: UUID = uuidString.deserializeAsUUID() assertFalse(uuid2 == UUID.getDefaultInstance()) - assertEquals(uuid, uuid1) assertEquals(uuid, uuid2) } @@ -120,23 +104,18 @@ class UUIDFactoryTest { } val version = uuid.getVersion() val time: Long? = uuid.getTime() - val bytes = MicroUuidSerializer.INSTANCE.serialize(uuid) - val uuidString = LongUuidSerializer.INSTANCE.serialize(uuid) + val uuidString = uuid.serialize() assertNotNull(uuid) assertFalse(uuid.isUuidv6()) assertFalse(uuid.isUProtocol()) assertFalse(uuid.isUuid()) assertNotNull(version) assertNull(time) - assertTrue(bytes.isNotEmpty()) - assertFalse(uuidString.isBlank()) - val uuid1: UUID = MicroUuidSerializer.INSTANCE.deserialize(bytes) - val uuid2: UUID = LongUuidSerializer.INSTANCE.deserialize(uuidString) + assertFalse(uuidString.isBlank()) - assertFalse(uuid1 == UUID.getDefaultInstance()) + val uuid2: UUID = uuidString.deserializeAsUUID() assertFalse(uuid2 == UUID.getDefaultInstance()) - assertEquals(uuid, uuid1) assertEquals(uuid, uuid2) } @@ -149,32 +128,20 @@ class UUIDFactoryTest { } val version = uuid.getVersion() val time: Long? = uuid.getTime() - val bytes = MicroUuidSerializer.INSTANCE.serialize(uuid) - val uuidString = LongUuidSerializer.INSTANCE.serialize(uuid) + val uuidString = uuid.serialize() assertNotNull(uuid) assertFalse(uuid.isUuidv6()) assertFalse(uuid.isUProtocol()) assertNotNull(version) assertEquals(UUIDVersion.VERSION_UNKNOWN, version) assertNull(time) - assertTrue(bytes.isNotEmpty()) assertFalse(uuidString.isBlank()) - val uuid1: UUID = MicroUuidSerializer.INSTANCE.deserialize(bytes) - assertTrue(uuid1 == UUID.getDefaultInstance()) - assertEquals(uuid, uuid1) - val uuid2: UUID = LongUuidSerializer.INSTANCE.deserialize(uuidString) + val uuid2: UUID = uuidString.deserializeAsUUID() assertTrue(uuid2 == UUID.getDefaultInstance()) assertEquals(uuid, uuid2) } - @Test - @DisplayName("Test UuidUtils for a null UUID") - fun test_uuidutils_for_null_uuid() { - assertTrue(MicroUuidSerializer.INSTANCE.serialize(null).isEmpty()) - assertTrue(LongUuidSerializer.INSTANCE.serialize(null).isBlank()) - } - @Test @DisplayName("Test UuidUtils fromString an invalid built UUID") fun test_uuidutils_from_invalid_uuid() { @@ -184,8 +151,7 @@ class UUIDFactoryTest { } // Invalid UUID type assertEquals(UUIDVersion.VERSION_UNKNOWN, uuid.getVersion()) assertNull(uuid.getTime()) - assertTrue(MicroUuidSerializer.INSTANCE.serialize(uuid).isNotEmpty()) - assertFalse(LongUuidSerializer.INSTANCE.serialize(uuid).isBlank()) + assertFalse(uuid.serialize().isBlank()) assertFalse(uuid.isUuidv6()) assertFalse(uuid.isUProtocol()) assertFalse(uuid.isUuid()) @@ -193,19 +159,8 @@ class UUIDFactoryTest { @Test @DisplayName("Test UuidUtils fromString with invalid string") - fun test_uuidutils_fromstring_with_invalid_string() { - val uuid: UUID = LongUuidSerializer.INSTANCE.deserialize(null) - assertTrue(uuid == UUID.getDefaultInstance()) - val uuid1: UUID = LongUuidSerializer.INSTANCE.deserialize("") - assertTrue(uuid1 == UUID.getDefaultInstance()) - } - - @Test - @DisplayName("Test UuidUtils fromBytes with invalid bytes") - fun test_uuidutils_frombytes_with_invalid_bytes() { - val uuid: UUID = MicroUuidSerializer.INSTANCE.deserialize(null) - assertTrue(uuid == UUID.getDefaultInstance()) - val uuid1: UUID = MicroUuidSerializer.INSTANCE.deserialize(ByteArray(0)) + fun test_uuidutils_from_string_with_invalid_string() { + val uuid1: UUID = "".deserializeAsUUID() assertTrue(uuid1 == UUID.getDefaultInstance()) } diff --git a/src/test/kotlin/org/eclipse/uprotocol/uuid/factory/UuidUtilsTest.kt b/src/test/kotlin/org/eclipse/uprotocol/uuid/factory/UuidUtilsTest.kt index 178d54b..21054a5 100644 --- a/src/test/kotlin/org/eclipse/uprotocol/uuid/factory/UuidUtilsTest.kt +++ b/src/test/kotlin/org/eclipse/uprotocol/uuid/factory/UuidUtilsTest.kt @@ -1,51 +1,32 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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-FileType: SOURCE - * SPDX-FileCopyrightText: 2024 General Motors GTO LLC * SPDX-License-Identifier: Apache-2.0 */ + package org.eclipse.uprotocol.uuid.factory import org.eclipse.uprotocol.v1.* import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import java.time.Instant import kotlin.math.abs import kotlin.test.assertNull class UuidUtilsTest { - - private val testSource = uUri { - entity = uEntity { name = "body.access" } - resource = uResource { - name = "door" - instance = "front_left" - message = "Door" - } - } - @Test @Throws(InterruptedException::class) fun testGetElapsedTime() { val testID = createId() Thread.sleep(DELAY_MS.toLong()) - //assertEquals(DELAY_MS, testID.getElapsedTime()?.toInt(), DELTA) assertEquals(DELAY_MS, testID.getElapsedTime()?.toInt(), DELTA) } @@ -82,37 +63,6 @@ class UuidUtilsTest { assertNull(id.getRemainingTime(DELAY_MS - DELTA)) } - @Test - @Throws(InterruptedException::class) - fun testGetRemainingTimeAttributes() { - val attributes: UAttributes = uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - ttl = TTL - } - assertEquals(TTL, attributes.getRemainingTime()?.toInt(), DELTA) - Thread.sleep(DELAY_MS.toLong()) - assertEquals(TTL - DELAY_MS, attributes.getRemainingTime()?.toInt(), DELTA) - } - - @Test - fun testGetRemainingTimeAttributesNoTtl() { - val attributes: UAttributes = uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - } - assertNull(attributes.getRemainingTime()) - } - - @Test - @Throws(InterruptedException::class) - fun testGetRemainingTimeAttributesExpired() { - val attributes: UAttributes = uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - ttl = DELAY_MS - DELTA - } - Thread.sleep(DELAY_MS.toLong()) - assertNull(attributes.getRemainingTime()) - } - @Test @Throws(InterruptedException::class) fun testIsExpired() { @@ -130,23 +80,11 @@ class UuidUtilsTest { } @Test - @Throws(InterruptedException::class) - fun testIsExpiredAttributes() { - val attributes: UAttributes = uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - ttl = DELAY_MS - DELTA - } - assertFalse(attributes.isExpired()) - Thread.sleep(DELAY_MS.toLong()) - assertTrue(attributes.isExpired()) - } - - @Test - fun testIsExpiredAttributesNoTtl() { - val attributes: UAttributes = uAttributes { - forPublication(testSource, UPriority.UPRIORITY_CS0) - } - assertFalse(attributes.isExpired()) + @DisplayName("Test getElapseTime() when UUID time is in the future") + fun testGetElapsedTimePast() { + val now = Instant.now().plusMillis(DELAY_MS.toLong()) + val id: UUID = UUIDV8(now) + assertNull(id.getElapsedTime()) } private fun assertEquals(expect: Int, actual: Int?, delta: Int?) { diff --git a/src/test/kotlin/org/eclipse/uprotocol/uuid/serializer/UuidSerializerKtTest.kt b/src/test/kotlin/org/eclipse/uprotocol/uuid/serializer/UuidSerializerKtTest.kt new file mode 100644 index 0000000..5101940 --- /dev/null +++ b/src/test/kotlin/org/eclipse/uprotocol/uuid/serializer/UuidSerializerKtTest.kt @@ -0,0 +1,45 @@ +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.uprotocol.uuid.serializer + +import org.eclipse.uprotocol.v1.UUID +import org.eclipse.uprotocol.v1.uUID +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class UuidSerializerTest { + @Test + @DisplayName("Test serializer with good uuid") + fun test_serializer_with_good_uuid() { + val uuidStr = "123e4567-e89b-12d3-a456-426614174000" + val uuid: UUID = uuidStr.deserializeAsUUID() + assertEquals(uuidStr, uuid.serialize()) + } + + @Test + @DisplayName("Test serializer with empty uuid") + fun test_serializer_with_empty_uuid() { + val uuid = uUID { } + assertEquals("00000000-0000-0000-0000-000000000000", uuid.serialize()) + } + + @Test + @DisplayName("Test deserializer with invalid uuid") + fun test_deserializer_with_invalid_uuid() { + val uuidStr = "sdsadfasdfsfgagASDfadasfgsdfgs" + val uuid: UUID = uuidStr.deserializeAsUUID() + assertEquals(uUID { }, uuid) + } +} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/uuid/validator/UuidValidatorTest.kt b/src/test/kotlin/org/eclipse/uprotocol/uuid/validator/UuidValidatorTest.kt index 0f0f2f9..0df982a 100644 --- a/src/test/kotlin/org/eclipse/uprotocol/uuid/validator/UuidValidatorTest.kt +++ b/src/test/kotlin/org/eclipse/uprotocol/uuid/validator/UuidValidatorTest.kt @@ -1,30 +1,23 @@ -/* - * Copyright (c) 2023 General Motors GTO LLC +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://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.uprotocol.uuid.validator import org.eclipse.uprotocol.uuid.factory.UUIDV6 import org.eclipse.uprotocol.uuid.factory.UUIDV8 import org.eclipse.uprotocol.uuid.factory.isUuidv6 -import org.eclipse.uprotocol.uuid.serializer.LongUuidSerializer +import org.eclipse.uprotocol.uuid.serializer.deserializeAsUUID import org.eclipse.uprotocol.uuid.validate.UUIDv6Validator import org.eclipse.uprotocol.uuid.validate.UUIDv8Validator import org.eclipse.uprotocol.uuid.validate.UuidValidator @@ -115,7 +108,7 @@ class UuidValidatorTest { @Test @DisplayName("Test UUIDv6 with bad variant") fun test_uuidv6_with_bad_variant() { - val uuid: UUID = LongUuidSerializer.INSTANCE.deserialize("1ee57e66-d33a-65e0-4a77-3c3f061c1e9e") + val uuid: UUID = "1ee57e66-d33a-65e0-4a77-3c3f061c1e9e".deserializeAsUUID() assertFalse(uuid == UUID.getDefaultInstance()) val validator: UuidValidator = UuidValidator.getValidator(uuid) assertNotNull(validator) diff --git a/src/test/kotlin/org/eclipse/uprotocol/v1/UPayloadKtExtTest.kt b/src/test/kotlin/org/eclipse/uprotocol/v1/UPayloadKtExtTest.kt deleted file mode 100644 index 2ba2ed1..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/v1/UPayloadKtExtTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package org.eclipse.uprotocol.v1 - -import com.google.protobuf.Any -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNotNull -import org.junit.jupiter.api.Test - - -class UPayloadKtExtTest { - @Test - fun testPackToAny() { - val uStatus = uStatus { } - val uPayload = uPayload { - packToAny(uStatus) - } - assertNotNull(uPayload) - assertEquals(UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY, uPayload.format) - assertEquals( - Any.pack(uStatus).toByteString(), - uPayload.value - ) - } - - @Test - fun testPack() { - val uStatus = uStatus { } - val uPayload = uPayload { - pack(uStatus) - } - assertNotNull(uPayload) - assertEquals(UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, uPayload.format) - assertEquals(uStatus.toByteString(), uPayload.value) - } -} diff --git a/src/test/kotlin/org/eclipse/uprotocol/v1/UResourceKtExtTest.kt b/src/test/kotlin/org/eclipse/uprotocol/v1/UResourceKtExtTest.kt deleted file mode 100644 index 14ed0fd..0000000 --- a/src/test/kotlin/org/eclipse/uprotocol/v1/UResourceKtExtTest.kt +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2024 General Motors GTO LLC - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package org.eclipse.uprotocol.v1 - -import org.eclipse.uprotocol.uServiceTopic -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.Test -import kotlin.random.Random - - -class UResourceFactoryTest { - @Test - fun test_forRpcResponse() { - val resource = uResource { - forRpcResponse() - } - assertEquals("rpc", resource.name) - assertEquals("response", resource.instance) - assertEquals(0, resource.id) - } - - @Test - fun test_forRpcRequest_without_argu() { - val resource = uResource { - forRpcRequest() - } - assertEquals("rpc", resource.name) - assertEquals("", resource.instance) - assertEquals(0, resource.id) - } - - @Test - fun test_forRpcRequest_with_method() { - val resource = uResource { - forRpcRequest("test") - } - assertEquals("rpc", resource.name) - assertEquals("test", resource.instance) - assertEquals(0, resource.id) - } - - @Test - fun test_forRpcRequest_with_id() { - val resource = uResource { - forRpcRequest(id = 999) - } - assertEquals("rpc", resource.name) - assertEquals("", resource.instance) - assertEquals(999, resource.id) - } - - @Test - fun test_forRpcRequest() { - val resource = uResource { - forRpcRequest(method = "test", id = 999) - } - assertEquals("rpc", resource.name) - assertEquals("test", resource.instance) - assertEquals(999, resource.id) - } - - @Test - fun test_fromId_valid_id() { - val idTest = Random.nextInt(0, 999) - val resource = uResource { - from(idTest) - } - assertEquals("rpc", resource.name) - assertEquals("", resource.instance) - assertEquals(idTest, resource.id) - } - - @Test - fun test_fromId_invalid_id() { - val idTest = Random.nextInt(1000, Int.MAX_VALUE) - val resource = uResource { - from(idTest) - } - assertEquals("", resource.name) - assertEquals("", resource.instance) - assertEquals(idTest, resource.id) - } - - @Test - fun test_from_uservice_topic_valid_service_topic() { - val topic = uServiceTopic { - name = "SubscriptionChange" - id = 0 - message = "Update" - } - val resource: UResource = uResource { - from(topic) - } - assertEquals(resource.name, "SubscriptionChange") - assertEquals(resource.instance, "") - assertEquals(resource.id, 0) - assertEquals(resource.message, "Update") - } - - @Test - fun test_from_uservice_topic_valid_service_topic_with_instance() { - val topic = uServiceTopic { - name = "door.front_left" - id = 0x8000 - message = "Door" - } - - val resource: UResource = uResource { - from(topic) - } - assertEquals(resource.name, "door") - assertEquals(resource.instance, "front_left") - assertEquals(resource.id, 0x8000) - assertEquals(resource.message, "Door") - } -} \ No newline at end of file diff --git a/src/test/kotlin/org/eclipse/uprotocol/validation/ValidationResultTest.kt b/src/test/kotlin/org/eclipse/uprotocol/validation/ValidationResultTest.kt new file mode 100644 index 0000000..4931414 --- /dev/null +++ b/src/test/kotlin/org/eclipse/uprotocol/validation/ValidationResultTest.kt @@ -0,0 +1,43 @@ +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.uprotocol.validation + +import org.eclipse.uprotocol.v1.UCode +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class ValidationResultTest { + @Test + @DisplayName("Test creating a successful ValidationResult") + fun testCreateSuccess() { + val result = ValidationResult.success() + Assertions.assertTrue(result.isSuccess()) + Assertions.assertFalse(result.isFailure()) + Assertions.assertEquals("", result.getMessage()) + Assertions.assertEquals(result.toStatus().code, UCode.OK) + Assertions.assertEquals(result.toString(), "ValidationResult.Success()") + } + + @Test + @DisplayName("Test creating a failed ValidationResult") + fun testCreateFailure() { + val result = ValidationResult.failure("Failed") + Assertions.assertFalse(result.isSuccess()) + Assertions.assertTrue(result.isFailure()) + Assertions.assertEquals("Failed", result.getMessage()) + Assertions.assertEquals(result.toStatus().code, UCode.INVALID_ARGUMENT) + Assertions.assertEquals(result.toString(), "ValidationResult.Failure(message='Failed')") + } +} \ No newline at end of file