2 * ============LICENSE_START=======================================================
3 * dcaegen2-collectors-veshv
4 * ================================================================================
5 * Copyright (C) 2018 NOKIA
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
20 package org.onap.dcae.collectors.veshv.domain
22 import arrow.core.Either
23 import arrow.core.identity
24 import io.netty.buffer.Unpooled
25 import io.netty.buffer.UnpooledByteBufAllocator
26 import org.assertj.core.api.Assertions.assertThat
27 import org.assertj.core.api.Assertions.fail
28 import org.assertj.core.api.ObjectAssert
29 import org.jetbrains.spek.api.Spek
30 import org.jetbrains.spek.api.dsl.describe
31 import org.jetbrains.spek.api.dsl.given
32 import org.jetbrains.spek.api.dsl.it
33 import org.onap.dcae.collectors.veshv.domain.WireFrameDecoder.Companion.MAX_PAYLOAD_SIZE
34 import java.nio.charset.Charset
35 import kotlin.test.assertTrue
38 * @author Piotr Jaszczyk <piotr.jaszczyk@nokia.com>
41 object WireFrameCodecsTest : Spek({
42 val payloadAsString = "coffeebabe"
43 val encoder = WireFrameEncoder(UnpooledByteBufAllocator.DEFAULT)
44 val decoder = WireFrameDecoder()
46 fun createSampleFrame() =
47 WireFrame(payloadAsString.toByteArray(Charset.defaultCharset()))
49 fun encodeSampleFrame() =
50 createSampleFrame().let {
54 describe("Wire Frame invariants") {
56 given("input with unsupported version") {
57 val input = WireFrame(
58 payload = ByteData.EMPTY,
60 payloadTypeRaw = PayloadContentType.GOOGLE_PROTOCOL_BUFFER.hexValue,
63 it("should fail validation") {
64 assertThat(input.isValid()).isFalse()
68 given("input with unsupported payload type") {
69 val input = WireFrame(
70 payload = ByteData.EMPTY,
72 payloadTypeRaw = 0x69,
75 it("should fail validation") {
76 assertThat(input.isValid()).isFalse()
80 given("input with too small payload size") {
81 val input = WireFrame(
82 payload = ByteData(byteArrayOf(1, 2, 3)),
84 payloadTypeRaw = PayloadContentType.GOOGLE_PROTOCOL_BUFFER.hexValue,
87 it("should fail validation") {
88 assertThat(input.isValid()).isFalse()
92 given("input with too big payload size") {
93 val input = WireFrame(
94 payload = ByteData(byteArrayOf(1, 2, 3)),
96 payloadTypeRaw = PayloadContentType.GOOGLE_PROTOCOL_BUFFER.hexValue,
99 it("should fail validation") {
100 assertThat(input.isValid()).isFalse()
104 given("valid input") {
105 val payload = byteArrayOf(6, 9, 8, 6)
106 val input = WireFrame(
107 payload = ByteData(payload),
109 payloadTypeRaw = PayloadContentType.GOOGLE_PROTOCOL_BUFFER.hexValue,
110 payloadSize = payload.size)
112 it("should pass validation") {
113 assertThat(input.isValid()).isTrue()
120 describe("Wire Frame codec") {
122 describe("encode-decode methods' compatibility") {
123 val frame = createSampleFrame()
124 val encoded = encodeSampleFrame()
125 val decoded = decoder.decodeFirst(encoded).getOrFail()
127 it("should decode version") {
128 assertThat(decoded.version).isEqualTo(frame.version)
131 it("should decode payload type") {
132 assertThat(decoded.payloadTypeRaw).isEqualTo(frame.payloadTypeRaw)
135 it("should decode payload size") {
136 assertThat(decoded.payloadSize).isEqualTo(frame.payloadSize)
139 it("should decode payload") {
140 assertThat(decoded.payload.asString())
141 .isEqualTo(payloadAsString)
145 describe("TCP framing") {
146 // see "Dealing with a Stream-based Transport" on http://netty.io/wiki/user-guide-for-4.x.html#wiki-h3-11
148 it("should decode message leaving rest unread") {
149 val buff = Unpooled.buffer()
150 .writeBytes(encodeSampleFrame())
152 val decoded = decoder.decodeFirst(buff).getOrFail()
154 assertThat(decoded.isValid()).describedAs("should be valid").isTrue()
155 assertThat(buff.readableBytes()).isEqualTo(1)
158 it("should return error when not even header fits") {
159 val buff = Unpooled.buffer()
162 decoder.decodeFirst(buff).assertFailedWithError { it.isInstanceOf(MissingWireFrameHeaderBytes::class.java) }
166 it("should return error when first byte is not 0xFF but length looks ok") {
167 val buff = Unpooled.buffer()
169 .writeBytes("some garbage".toByteArray())
171 decoder.decodeFirst(buff).assertFailedWithError { it.isInstanceOf(InvalidWireFrameMarker::class.java) }
174 it("should return error when first byte is not 0xFF and length is to short") {
175 val buff = Unpooled.buffer()
178 decoder.decodeFirst(buff).assertFailedWithError { it.isInstanceOf(MissingWireFrameHeaderBytes::class.java) }
181 it("should return error when payload doesn't fit") {
182 val buff = Unpooled.buffer()
183 .writeBytes(encodeSampleFrame())
184 buff.writerIndex(buff.writerIndex() - 2)
186 decoder.decodeFirst(buff).assertFailedWithError { it.isInstanceOf(MissingWireFramePayloadBytes::class.java) }
191 describe("payload size limit"){
193 it("should decode successfully when payload size is equal 1 MiB") {
195 val payload = ByteArray(MAX_PAYLOAD_SIZE)
196 val input = WireFrame(
197 payload = ByteData(payload),
199 payloadTypeRaw = PayloadContentType.GOOGLE_PROTOCOL_BUFFER.hexValue,
200 payloadSize = payload.size)
203 assertTrue(decoder.decodeFirst(encoder.encode(input)).isRight())
206 it("should return error when payload exceeds 1 MiB") {
208 val payload = ByteArray(MAX_PAYLOAD_SIZE + 1)
209 val input = WireFrame(
210 payload = ByteData(payload),
212 payloadTypeRaw = PayloadContentType.GOOGLE_PROTOCOL_BUFFER.hexValue,
213 payloadSize = payload.size)
216 decoder.decodeFirst(encoder.encode(input))
217 .assertFailedWithError { it.isInstanceOf(PayloadSizeExceeded::class.java) }
220 it("should validate only first message") {
222 val payload = ByteArray(MAX_PAYLOAD_SIZE)
223 val input = WireFrame(
224 payload = ByteData(payload),
226 payloadTypeRaw = PayloadContentType.GOOGLE_PROTOCOL_BUFFER.hexValue,
227 payloadSize = payload.size)
230 assertTrue(decoder.decodeFirst(encoder.encode(input).writeByte(0xFF)).isRight())
236 private fun <A, B> Either<A, B>.assertFailedWithError(assertj: (ObjectAssert<A>) -> Unit) {
237 fold({ assertj(assertThat(it)) }, { fail("Error expected") })
240 private fun Either<WireFrameDecodingError, WireFrame>.getOrFail(): WireFrame =
241 fold({ fail(it.message) }, ::identity) as WireFrame