Skip to content

Commit

Permalink
[TH2-2394] Added the ability to verify repeating groups according to …
Browse files Browse the repository at this point in the history
…defined filters (#74)

* [TH2-2394] Added the ability to verify repeating groups according to defined filters

* [TH2-2394] Refactored tests with comparing legs

* [TH2-2394] Small refactoring

* [TH2-2394] Fixed verification entries assertion
  • Loading branch information
yarchiksmith authored Oct 4, 2021
1 parent 6e2967f commit 10c0765
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 33 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,13 @@ The `th2_check1_active_tasks_number` metric separate rules with label `rule_type
+ Added check for required message type in the message filter
+ Provided more detailed logging in comparable messages
+ Provided the ability to attach verification description to event
+ Provided the ability to verify repeating groups according to defined filters via `check_repeating_group_order` parameter in the `RootComparisonSettings` message

#### Changed:
+ Migrated `common` version from `3.25.0` to `3.26.3`
+ Migrated `common` version from `3.25.0` to `3.26.4`
+ Added support for converting SimpleList to readable payload body
+ Added the new `description` parameter to `RootMessageFilter` message
+ Migrated `grpc-check1` version from `3.2.0` to `3.4.1`
+ Migrated `grpc-check1` version from `3.2.0` to `3.4.2`
+ Migrated sailfish-utils from `3.7.0` to `3.8.1`
+ Now Check1 keep the order of repeating result groups by default
+ Fix IN, NOT_IN FilterOperation interaction
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ signing {

dependencies {
api platform('com.exactpro.th2:bom:3.0.0')
implementation 'com.exactpro.th2:grpc-check1:3.4.1'
implementation 'com.exactpro.th2:common:3.26.3'
implementation 'com.exactpro.th2:grpc-check1:3.4.2'
implementation 'com.exactpro.th2:common:3.26.4'
implementation 'com.exactpro.th2:sailfish-utils:3.9.1'
implementation "org.slf4j:slf4j-log4j12"
implementation "org.slf4j:slf4j-api"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,11 @@ abstract class AbstractCheckTask(
ComparatorSettings().also {
it.metaContainer = VerificationUtil.toMetaContainer(this.messageFilter, false)
it.ignoredFields = this.comparisonSettings.ignoreFieldsList.toSet()
it.isKeepResultGroupOrder = true
if (this.comparisonSettings.checkRepeatingGroupOrder) {
it.isCheckGroupsOrder = true
} else {
it.isKeepResultGroupOrder = true
}
}

protected fun MessageFilter.toCompareSettings(): ComparatorSettings =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,26 @@ package com.exactpro.th2.check1.rule

import com.exactpro.th2.check1.SessionKey
import com.exactpro.th2.check1.StreamContainer
import com.exactpro.th2.common.event.IBodyData
import com.exactpro.th2.common.event.bean.Verification
import com.exactpro.th2.common.event.bean.VerificationEntry
import com.exactpro.th2.common.grpc.Direction
import com.exactpro.th2.common.grpc.Direction.FIRST
import com.exactpro.th2.common.grpc.Event
import com.exactpro.th2.common.grpc.EventBatch
import com.exactpro.th2.common.grpc.Message
import com.exactpro.th2.common.schema.message.MessageRouter
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.nhaarman.mockitokotlin2.argumentCaptor
import com.nhaarman.mockitokotlin2.spy
import com.nhaarman.mockitokotlin2.timeout
import com.nhaarman.mockitokotlin2.verify
import io.reactivex.Observable
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

abstract class AbstractCheckTaskTest {
protected val clientStub: MessageRouter<EventBatch> = spy { }
Expand Down Expand Up @@ -51,6 +61,56 @@ abstract class AbstractCheckTaskTest {
}
}

protected fun extractEventBody(verificationEvent: Event): List<IBodyData> {
return jacksonObjectMapper()
.addMixIn(IBodyData::class.java, IBodyDataMixIn::class.java)
.readValue(verificationEvent.body.toByteArray())
}

protected fun assertVerification(verificationEvent: Event): Verification {
val verifications = extractEventBody(verificationEvent)
val verification = verifications.filterIsInstance<Verification>().firstOrNull()
assertNotNull(verification) { "Verification event does not contain the verification" }
return verification
}

protected fun assertVerificationEntries(
expectedVerificationEntries: Map<String, VerificationEntry>,
actualVerificationEntries: Map<String, VerificationEntry>?,
asserts: (VerificationEntry, VerificationEntry) -> Unit) {
assertNotNull(actualVerificationEntries) { "Actual verification entry is null" }
expectedVerificationEntries.forEach { (expectedFieldName, expectedVerificationEntry) ->
val actualVerificationEntry = actualVerificationEntries[expectedFieldName]
assertNotNull(actualVerificationEntry) {
"Actual verification entry does not contains field '${expectedFieldName}'"
}
asserts(expectedVerificationEntry, actualVerificationEntry)
if (expectedVerificationEntry.fields == null) {
return@forEach
}
assertVerificationEntries(expectedVerificationEntry.fields, actualVerificationEntry.fields, asserts)
}
}

protected fun assertVerifications(
expectedVerification: Verification,
actualVerification: Verification,
asserts: (VerificationEntry, VerificationEntry) -> Unit) = assertVerificationEntries(expectedVerification.fields, actualVerification.fields, asserts)

protected fun assertVerificationByStatus(verification: Verification, expectedVerificationEntries: Map<String, VerificationEntry>) {
assertVerificationEntries(expectedVerificationEntries, verification.fields) { expected, actual ->
assertEquals(expected.status, actual.status)
}
}


@JsonSubTypes(value = [
JsonSubTypes.Type(value = Verification::class, name = Verification.TYPE),
JsonSubTypes.Type(value = com.exactpro.th2.common.event.bean.Message::class, name = com.exactpro.th2.common.event.bean.Message.TYPE)
])
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.PROPERTY, visible = true)
private interface IBodyDataMixIn

companion object {
const val MESSAGE_TYPE = "TestMsg"
const val SESSION_ALIAS = "test_session"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ package com.exactpro.th2.check1.rule.check
import com.exactpro.th2.check1.SessionKey
import com.exactpro.th2.check1.StreamContainer
import com.exactpro.th2.check1.rule.AbstractCheckTaskTest
import com.exactpro.th2.check1.util.createVerificationEntry
import com.exactpro.th2.check1.util.toSimpleFilter
import com.exactpro.th2.common.event.bean.Verification
import com.exactpro.th2.common.event.bean.VerificationStatus
import com.exactpro.th2.common.grpc.Direction
import com.exactpro.th2.common.grpc.EventBatch
Expand All @@ -28,6 +28,7 @@ import com.exactpro.th2.common.grpc.FilterOperation
import com.exactpro.th2.common.grpc.ListValueFilter
import com.exactpro.th2.common.grpc.MessageMetadata
import com.exactpro.th2.common.grpc.MetadataFilter
import com.exactpro.th2.common.grpc.RootComparisonSettings
import com.exactpro.th2.common.grpc.RootMessageFilter
import com.exactpro.th2.common.grpc.ValueFilter
import com.exactpro.th2.common.message.message
Expand All @@ -37,8 +38,6 @@ import com.exactpro.th2.common.value.add
import com.exactpro.th2.common.value.listValue
import com.exactpro.th2.common.value.toValue
import com.exactpro.th2.common.value.toValueFilter
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.google.protobuf.StringValue
import io.reactivex.Observable
import org.junit.jupiter.api.Assertions.assertTrue
Expand Down Expand Up @@ -234,7 +233,6 @@ internal class TestCheckRuleTask : AbstractCheckTaskTest() {
assertEquals("'timeout' should be set or be greater than zero, actual: $timeout", exception.message)
}


@Test
fun `check that the order is kept in repeating groups`() {
val streams = createStreams(SESSION_ALIAS, Direction.FIRST, listOf(
Expand Down Expand Up @@ -289,32 +287,22 @@ internal class TestCheckRuleTask : AbstractCheckTaskTest() {
}, {
val verificationEvent = eventList.find { it.type == "Verification" }
assertNotNull(verificationEvent) { "Missed verification event" }

val verification = jacksonObjectMapper().readValue<List<Verification>>(verificationEvent.body.toByteArray()).firstOrNull()
assertNotNull(verification) { "Verification event does not contain the verification" }
val actualLegs = verification.fields["legs"]?.fields?.values?.toList()
assertNotNull(actualLegs) { "Actual legs is missed" }

val expectedLegs = linkedMapOf(
0 to linkedMapOf(
"A" to VerificationStatus.PASSED,
"B" to VerificationStatus.FAILED
),
1 to linkedMapOf(
"A" to VerificationStatus.PASSED,
"B" to VerificationStatus.PASSED
val verification = assertVerification(verificationEvent)

val expectedLegs = mapOf(
"legs" to createVerificationEntry(
"0" to createVerificationEntry(
"A" to createVerificationEntry(VerificationStatus.PASSED),
"B" to createVerificationEntry(VerificationStatus.FAILED)
),
"1" to createVerificationEntry(
"A" to createVerificationEntry(VerificationStatus.PASSED),
"B" to createVerificationEntry(VerificationStatus.PASSED)
)
)
)

expectedLegs.forEach { (leg, verificationEntryByField) ->
val actualLeg = actualLegs[leg].fields
assertNotNull(actualLeg) { "The validation event does not contain the expected leg" }
verificationEntryByField.forEach { (field, status) ->
val expectedVerificationEntry = actualLeg[field]
assertNotNull(expectedVerificationEntry) { "Actual leg does not contain the expected field" }
assertEquals(status, expectedVerificationEntry.status)
}
}
assertVerificationByStatus(verification, expectedLegs)
})
}

Expand Down Expand Up @@ -348,6 +336,87 @@ internal class TestCheckRuleTask : AbstractCheckTaskTest() {
})
}

@Test
fun `verify repeating groups according to defined filters`() {
val streams = createStreams(SESSION_ALIAS, Direction.FIRST, listOf(
message(MESSAGE_TYPE, Direction.FIRST, SESSION_ALIAS)
.putFields("legs", listValue()
.add(message(MESSAGE_TYPE, Direction.FIRST, SESSION_ALIAS)
.putAllFields(mapOf(
"A" to "1".toValue(),
"B" to "2".toValue()
)))
.add(message(MESSAGE_TYPE, Direction.FIRST, SESSION_ALIAS)
.putAllFields(mapOf(
"C" to "3".toValue(),
"D" to "4".toValue()
)))
.toValue())
.build()
))

val messageFilterForCheckOrder: RootMessageFilter = rootMessageFilter(MESSAGE_TYPE).apply {
comparisonSettings = RootComparisonSettings.newBuilder().apply {
checkRepeatingGroupOrder = true
}.build()
messageFilter = messageFilter().apply {
putFields("legs", ValueFilter.newBuilder()
.setListFilter(ListValueFilter.newBuilder().apply {
addValues(ValueFilter.newBuilder().apply {
messageFilter = messageFilter().apply {
putAllFields(mapOf(
"C" to "3".toValueFilter(),
"D" to "4".toValueFilter()
))
}.build()
}.build())
addValues(ValueFilter.newBuilder().apply {
messageFilter = messageFilter().apply {
putAllFields(mapOf(
"A" to "1".toValueFilter(),
"B" to "2".toValueFilter()
))
}.build()
}.build())
}.build()).build())
}.build()
}.build()

val eventID = EventID.newBuilder().setId("root").build()

checkTask(messageFilterForCheckOrder, eventID, streams).begin()

val eventBatches = awaitEventBatchRequest(1000L, 2)
val eventList = eventBatches.flatMap(EventBatch::getEventsList)
assertAll({
assertEquals(3, eventList.size)
}, {
val verificationEvent = eventList.find { it.type == "Verification" }
assertNotNull(verificationEvent) { "Missed verification event" }

val verification = assertVerification(verificationEvent)

val expectedLegs = mapOf(
"legs" to createVerificationEntry(
"0" to createVerificationEntry(
"A" to createVerificationEntry(VerificationStatus.NA),
"B" to createVerificationEntry(VerificationStatus.NA),
"C" to createVerificationEntry(VerificationStatus.FAILED),
"D" to createVerificationEntry(VerificationStatus.FAILED)
),
"1" to createVerificationEntry(
"C" to createVerificationEntry(VerificationStatus.NA),
"D" to createVerificationEntry(VerificationStatus.NA),
"A" to createVerificationEntry(VerificationStatus.FAILED),
"B" to createVerificationEntry(VerificationStatus.FAILED)
)
)
)

assertVerificationByStatus(verification, expectedLegs)
})
}


companion object {
private const val VERIFICATION_DESCRIPTION = "Test verification with description"
Expand Down
13 changes: 12 additions & 1 deletion src/test/kotlin/com/exactpro/th2/check1/util/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,22 @@

package com.exactpro.th2.check1.util

import com.exactpro.th2.common.event.bean.VerificationEntry
import com.exactpro.th2.common.event.bean.VerificationStatus
import com.exactpro.th2.common.grpc.FilterOperation
import com.exactpro.th2.common.grpc.MetadataFilter

fun String.toSimpleFilter(op: FilterOperation, key: Boolean = false): MetadataFilter.SimpleFilter = MetadataFilter.SimpleFilter.newBuilder()
.setOperation(op)
.setValue(this)
.setKey(key)
.build()
.build()


fun createVerificationEntry(status: VerificationStatus): VerificationEntry = VerificationEntry().apply {
this.status = status
}

fun createVerificationEntry(vararg verificationEntries: Pair<String, VerificationEntry>): VerificationEntry = VerificationEntry().apply {
fields = linkedMapOf(*verificationEntries)
}

0 comments on commit 10c0765

Please sign in to comment.