From fd2c448e79008d0a859bcab290a522c2eaf50617 Mon Sep 17 00:00:00 2001 From: Yash Nabar Date: Mon, 20 Nov 2023 13:02:31 +0000 Subject: [PATCH 1/3] Add unit tests for Member/MGM context serializers, extract MemberInfo test utils --- libs/membership/membership-impl/build.gradle | 3 + .../membership/lib/impl/MemberInfoTest.kt | 105 ++---------------- .../amqp/MGMContextSerializerTest.kt | 37 ++++++ .../amqp/MemberContextSerializerTest.kt | 37 ++++++ .../lib/impl/utils/MemberInfoTestUtils.kt | 103 +++++++++++++++++ 5 files changed, 187 insertions(+), 98 deletions(-) create mode 100644 libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/serializer/amqp/MGMContextSerializerTest.kt create mode 100644 libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/serializer/amqp/MemberContextSerializerTest.kt create mode 100644 libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt diff --git a/libs/membership/membership-impl/build.gradle b/libs/membership/membership-impl/build.gradle index 96285545f43..0f37a0d54fd 100644 --- a/libs/membership/membership-impl/build.gradle +++ b/libs/membership/membership-impl/build.gradle @@ -24,8 +24,11 @@ dependencies { implementation "net.corda:corda-membership" implementation 'net.corda:corda-serialization' + testImplementation project(':libs:crypto:cipher-suite-impl') testImplementation project(":libs:crypto:crypto-impl") testImplementation project(":testing:layered-property-map-testkit") + testImplementation project(':testing:test-serialization') + testImplementation project(':libs:serialization:serialization-amqp') testImplementation project(":testing:test-utilities") testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion" diff --git a/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/MemberInfoTest.kt b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/MemberInfoTest.kt index e0e50c97c3c..7cdd9c45a74 100644 --- a/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/MemberInfoTest.kt +++ b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/MemberInfoTest.kt @@ -1,6 +1,5 @@ package net.corda.membership.lib.impl -import net.corda.crypto.cipher.suite.CipherSchemeMetadata import net.corda.crypto.impl.converter.PublicKeyConverter import net.corda.data.KeyValuePairList import net.corda.data.crypto.wire.CryptoSignatureSpec @@ -9,28 +8,17 @@ import net.corda.data.membership.SignedData import net.corda.data.membership.SignedMemberInfo import net.corda.layeredpropertymap.testkit.LayeredPropertyMapMocks import net.corda.layeredpropertymap.toAvro -import net.corda.membership.lib.EndpointInfoFactory -import net.corda.membership.lib.MemberInfoExtension.Companion.GROUP_ID import net.corda.membership.lib.MemberInfoExtension.Companion.LEDGER_KEYS -import net.corda.membership.lib.MemberInfoExtension.Companion.LEDGER_KEYS_KEY -import net.corda.membership.lib.MemberInfoExtension.Companion.MEMBER_STATUS_ACTIVE -import net.corda.membership.lib.MemberInfoExtension.Companion.MODIFIED_TIME -import net.corda.membership.lib.MemberInfoExtension.Companion.PARTY_NAME -import net.corda.membership.lib.MemberInfoExtension.Companion.PARTY_SESSION_KEYS -import net.corda.membership.lib.MemberInfoExtension.Companion.PLATFORM_VERSION -import net.corda.membership.lib.MemberInfoExtension.Companion.PROTOCOL_VERSION -import net.corda.membership.lib.MemberInfoExtension.Companion.SERIAL -import net.corda.membership.lib.MemberInfoExtension.Companion.SOFTWARE_VERSION -import net.corda.membership.lib.MemberInfoExtension.Companion.STATUS -import net.corda.membership.lib.MemberInfoExtension.Companion.URL_KEY import net.corda.membership.lib.MemberInfoExtension.Companion.endpoints import net.corda.membership.lib.MemberInfoExtension.Companion.groupId import net.corda.membership.lib.MemberInfoExtension.Companion.modifiedTime import net.corda.membership.lib.MemberInfoExtension.Companion.status import net.corda.membership.lib.impl.converter.EndpointInfoConverter import net.corda.membership.lib.impl.converter.MemberNotaryDetailsConverter +import net.corda.membership.lib.impl.utils.createDummyMemberInfo +import net.corda.membership.lib.impl.utils.keyEncodingService +import net.corda.membership.lib.impl.utils.ledgerKeys import net.corda.membership.lib.toSortedMap -import net.corda.test.util.time.TestClock import net.corda.utilities.parse import net.corda.utilities.parseList import net.corda.v5.base.exceptions.ValueNotFoundException @@ -45,16 +33,8 @@ import org.apache.avro.specific.SpecificDatumWriter import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test -import org.mockito.Mockito -import org.mockito.kotlin.any -import org.mockito.kotlin.doAnswer -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.mock -import org.mockito.kotlin.whenever import java.io.File import java.nio.ByteBuffer -import java.security.PublicKey -import java.time.Instant import kotlin.test.assertFailsWith import kotlin.test.assertNotNull import kotlin.test.assertNull @@ -63,26 +43,6 @@ import kotlin.test.assertTrue @Suppress("MaxLineLength") class MemberInfoTest { companion object { - private val keyEncodingService = Mockito.mock(CipherSchemeMetadata::class.java) - private const val KEY = "12345" - private val key = Mockito.mock(PublicKey::class.java) - - - private val clock = TestClock(Instant.ofEpochSecond(100)) - private val modifiedTime = clock.instant() - private val endpointInfoFactory: EndpointInfoFactory = mock { - on { create(any(), any()) } doAnswer { invocation -> - mock { - on { this.url } doReturn invocation.getArgument(0) - on { this.protocolVersion } doReturn invocation.getArgument(1) - } - } - } - private val endpoints = listOf( - endpointInfoFactory.create("https://localhost:10000"), - endpointInfoFactory.create("https://google.com", 10) - ) - private val ledgerKeys = listOf(key, key) private val testObjects = listOf( DummyObjectWithNumberAndText(1, "dummytext1"), DummyObjectWithNumberAndText(2, "dummytext2") @@ -98,59 +58,12 @@ class MemberInfoTest { DummyConverter() ) - private const val NULL_KEY = "nullKey" private const val DUMMY_KEY = "dummyKey" private const val INVALID_LIST_KEY = "invalidList" private val MemberInfo.dummy: List get() = memberProvidedContext.parseList(INVALID_LIST_KEY) - @Suppress("SpreadOperator") - private fun createDummyMemberInfo(): MemberInfo = MemberInfoImpl( - memberProvidedContext = LayeredPropertyMapMocks.create( - sortedMapOf( - PARTY_NAME to "O=Alice,L=London,C=GB", - String.format(PARTY_SESSION_KEYS, 0) to KEY, - GROUP_ID to "DEFAULT_MEMBER_GROUP_ID", - *convertPublicKeys().toTypedArray(), - *convertEndpoints().toTypedArray(), - *convertTestObjects().toTypedArray(), - *createInvalidListFormat().toTypedArray(), - SOFTWARE_VERSION to "5.0.0", - PLATFORM_VERSION to "5000", - DUMMY_KEY to "dummyValue", - NULL_KEY to null, - ), - converters - ), - mgmProvidedContext = LayeredPropertyMapMocks.create( - sortedMapOf( - STATUS to MEMBER_STATUS_ACTIVE, - MODIFIED_TIME to modifiedTime.toString(), - DUMMY_KEY to "dummyValue", - SERIAL to "1", - ), - converters - ) - ) - - private fun convertEndpoints(): List> { - val result = mutableListOf>() - for (i in endpoints.indices) { - result.add(Pair(String.format(URL_KEY, i), endpoints[i].url)) - result.add(Pair(String.format(PROTOCOL_VERSION, i), endpoints[i].protocolVersion.toString())) - } - return result - } - - private fun convertPublicKeys(): List> = - ledgerKeys.mapIndexed { i, ledgerKey -> - String.format( - LEDGER_KEYS_KEY, - i - ) to keyEncodingService.encodeAsString(ledgerKey) - } - private fun convertTestObjects(): List> { val result = mutableListOf>() for (i in testObjects.indices) { @@ -182,14 +95,10 @@ class MemberInfoTest { @BeforeAll @JvmStatic fun setUp() { - whenever( - keyEncodingService.decodePublicKey(KEY) - ).thenReturn(key) - whenever( - keyEncodingService.encodeAsString(key) - ).thenReturn(KEY) - - memberInfo = createDummyMemberInfo() + memberInfo = createDummyMemberInfo( + converters = converters, + additionalMemberInfoProperties = convertTestObjects() + createInvalidListFormat(), + ) } } diff --git a/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/serializer/amqp/MGMContextSerializerTest.kt b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/serializer/amqp/MGMContextSerializerTest.kt new file mode 100644 index 00000000000..dbb5aad1ff1 --- /dev/null +++ b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/serializer/amqp/MGMContextSerializerTest.kt @@ -0,0 +1,37 @@ +package net.corda.membership.lib.impl.serializer.amqp + +import net.corda.cipher.suite.impl.CipherSchemeMetadataImpl +import net.corda.crypto.impl.converter.PublicKeyConverter +import net.corda.internal.serialization.amqp.helper.TestSerializationService +import net.corda.layeredpropertymap.testkit.LayeredPropertyMapMocks +import net.corda.membership.lib.impl.converter.EndpointInfoConverter +import net.corda.membership.lib.impl.converter.MemberNotaryDetailsConverter +import net.corda.membership.lib.impl.utils.createDummyMgmContext +import net.corda.v5.membership.MGMContext +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test + +class MGMContextSerializerTest { + private companion object { + val cipherSchemeMetadata = CipherSchemeMetadataImpl() + val converters = listOf( + EndpointInfoConverter(), + MemberNotaryDetailsConverter(cipherSchemeMetadata), + PublicKeyConverter(cipherSchemeMetadata), + ) + val layeredPropertyMapFactory = LayeredPropertyMapMocks.createFactory(converters) + val mgmContextSerializer = MGMContextSerializer(layeredPropertyMapFactory) + + val serializationServiceWithWireTx = TestSerializationService.getTestSerializationService({ + it.register(mgmContextSerializer, it) + }, cipherSchemeMetadata) + } + + @Test + fun `Should serialize and then deserialize MGMContext`() { + val mgmContext = createDummyMgmContext(converters) as MGMContext + val serialized = serializationServiceWithWireTx.serialize(mgmContext) + val deserialized = serializationServiceWithWireTx.deserialize(serialized, MGMContext::class.java) + Assertions.assertThat(deserialized).isEqualTo(mgmContext) + } +} diff --git a/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/serializer/amqp/MemberContextSerializerTest.kt b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/serializer/amqp/MemberContextSerializerTest.kt new file mode 100644 index 00000000000..736b051187c --- /dev/null +++ b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/serializer/amqp/MemberContextSerializerTest.kt @@ -0,0 +1,37 @@ +package net.corda.membership.lib.impl.serializer.amqp + +import net.corda.cipher.suite.impl.CipherSchemeMetadataImpl +import net.corda.crypto.impl.converter.PublicKeyConverter +import net.corda.internal.serialization.amqp.helper.TestSerializationService +import net.corda.layeredpropertymap.testkit.LayeredPropertyMapMocks +import net.corda.membership.lib.impl.converter.EndpointInfoConverter +import net.corda.membership.lib.impl.converter.MemberNotaryDetailsConverter +import net.corda.membership.lib.impl.utils.createDummyMemberContext +import net.corda.v5.membership.MemberContext +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class MemberContextSerializerTest { + private companion object { + val cipherSchemeMetadata = CipherSchemeMetadataImpl() + val converters = listOf( + EndpointInfoConverter(), + MemberNotaryDetailsConverter(cipherSchemeMetadata), + PublicKeyConverter(cipherSchemeMetadata), + ) + val layeredPropertyMapFactory = LayeredPropertyMapMocks.createFactory(converters) + val memberContextSerializer = MemberContextSerializer(layeredPropertyMapFactory) + + val serializationServiceWithWireTx = TestSerializationService.getTestSerializationService({ + it.register(memberContextSerializer, it) + }, cipherSchemeMetadata) + } + + @Test + fun `Should serialize and then deserialize MemberContext`() { + val memberContext = createDummyMemberContext(converters) as MemberContext + val serialized = serializationServiceWithWireTx.serialize(memberContext) + val deserialized = serializationServiceWithWireTx.deserialize(serialized, MemberContext::class.java) + assertThat(deserialized).isEqualTo(memberContext) + } +} diff --git a/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt new file mode 100644 index 00000000000..eb587853e9f --- /dev/null +++ b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt @@ -0,0 +1,103 @@ +package net.corda.membership.lib.impl.utils + +import net.corda.crypto.cipher.suite.CipherSchemeMetadata +import net.corda.layeredpropertymap.CustomPropertyConverter +import net.corda.layeredpropertymap.testkit.LayeredPropertyMapMocks +import net.corda.membership.lib.EndpointInfoFactory +import net.corda.membership.lib.MemberInfoExtension +import net.corda.membership.lib.impl.MGMContextImpl +import net.corda.membership.lib.impl.MemberContextImpl +import net.corda.membership.lib.impl.MemberInfoImpl +import net.corda.test.util.time.TestClock +import net.corda.v5.membership.MemberInfo +import org.mockito.Mockito +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import java.security.PublicKey +import java.time.Instant + +private const val KEY = "12345" +private val key: PublicKey = Mockito.mock(PublicKey::class.java) +internal val keyEncodingService: CipherSchemeMetadata = mock { + on { decodePublicKey(KEY) } doReturn key + on { encodeAsString(key) } doReturn KEY +} + +private val clock = TestClock(Instant.ofEpochSecond(100)) +private val modifiedTime = clock.instant() +private val endpointInfoFactory: EndpointInfoFactory = mock { + on { create(any(), any()) } doAnswer { invocation -> + mock { + on { this.url } doReturn invocation.getArgument(0) + on { this.protocolVersion } doReturn invocation.getArgument(1) + } + } +} +private val endpoints = listOf( + endpointInfoFactory.create("https://localhost:10000"), endpointInfoFactory.create("https://google.com", 10) +) +internal val ledgerKeys = listOf(key, key) + +private const val NULL_KEY = "nullKey" +private const val DUMMY_KEY = "dummyKey" + +@Suppress("SpreadOperator") +fun createDummyMemberInfo( + converters: List>, + additionalMemberInfoProperties: List> = emptyList(), +): MemberInfo = MemberInfoImpl( + memberProvidedContext = createDummyMemberContext(converters, additionalMemberInfoProperties), + mgmProvidedContext = createDummyMgmContext(converters) +) + +@Suppress("SpreadOperator") +fun createDummyMemberContext( + converters: List>, + additionalMemberInfoProperties: List> = emptyList(), +) = LayeredPropertyMapMocks.create( + sortedMapOf( + MemberInfoExtension.PARTY_NAME to "O=Alice,L=London,C=GB", + String.format(MemberInfoExtension.PARTY_SESSION_KEYS, 0) to KEY, + MemberInfoExtension.GROUP_ID to "DEFAULT_MEMBER_GROUP_ID", + *convertPublicKeys().toTypedArray(), + *convertEndpoints().toTypedArray(), + *additionalMemberInfoProperties.toTypedArray(), + MemberInfoExtension.SOFTWARE_VERSION to "5.0.0", + MemberInfoExtension.PLATFORM_VERSION to "5000", + DUMMY_KEY to "dummyValue", + NULL_KEY to null, + ), converters +) + +fun createDummyMgmContext( + converters: List>, +) = LayeredPropertyMapMocks.create( + sortedMapOf( + MemberInfoExtension.STATUS to MemberInfoExtension.MEMBER_STATUS_ACTIVE, + MemberInfoExtension.MODIFIED_TIME to modifiedTime.toString(), + DUMMY_KEY to "dummyValue", + MemberInfoExtension.SERIAL to "1", + ), converters +) + +fun convertEndpoints(): List> { + val result = mutableListOf>() + for (i in endpoints.indices) { + result.add(Pair(String.format(MemberInfoExtension.URL_KEY, i), endpoints[i].url)) + result.add( + Pair( + String.format(MemberInfoExtension.PROTOCOL_VERSION, i), + endpoints[i].protocolVersion.toString() + ) + ) + } + return result +} + +fun convertPublicKeys(): List> = ledgerKeys.mapIndexed { i, ledgerKey -> + String.format( + MemberInfoExtension.LEDGER_KEYS_KEY, i + ) to keyEncodingService.encodeAsString(ledgerKey) +} From 2b015556efb0b4f993c8e743bcee3c90916a4383 Mon Sep 17 00:00:00 2001 From: Yash Nabar Date: Mon, 20 Nov 2023 15:06:54 +0000 Subject: [PATCH 2/3] Address comments --- .../net/corda/membership/lib/impl/MemberInfoTest.kt | 2 +- .../membership/lib/impl/utils/MemberInfoTestUtils.kt | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/MemberInfoTest.kt b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/MemberInfoTest.kt index 7cdd9c45a74..f5cce2229e7 100644 --- a/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/MemberInfoTest.kt +++ b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/MemberInfoTest.kt @@ -97,7 +97,7 @@ class MemberInfoTest { fun setUp() { memberInfo = createDummyMemberInfo( converters = converters, - additionalMemberInfoProperties = convertTestObjects() + createInvalidListFormat(), + additionalMemberContext = convertTestObjects() + createInvalidListFormat(), ) } } diff --git a/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt index eb587853e9f..ae995a971c7 100644 --- a/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt +++ b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt @@ -17,6 +17,7 @@ import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import java.security.PublicKey import java.time.Instant +import java.util.UUID private const val KEY = "12345" private val key: PublicKey = Mockito.mock(PublicKey::class.java) @@ -46,28 +47,28 @@ private const val DUMMY_KEY = "dummyKey" @Suppress("SpreadOperator") fun createDummyMemberInfo( converters: List>, - additionalMemberInfoProperties: List> = emptyList(), + additionalMemberContext: List> = emptyList(), ): MemberInfo = MemberInfoImpl( - memberProvidedContext = createDummyMemberContext(converters, additionalMemberInfoProperties), + memberProvidedContext = createDummyMemberContext(converters, additionalMemberContext), mgmProvidedContext = createDummyMgmContext(converters) ) @Suppress("SpreadOperator") fun createDummyMemberContext( converters: List>, - additionalMemberInfoProperties: List> = emptyList(), + additionalMemberContext: List> = emptyList(), ) = LayeredPropertyMapMocks.create( sortedMapOf( MemberInfoExtension.PARTY_NAME to "O=Alice,L=London,C=GB", String.format(MemberInfoExtension.PARTY_SESSION_KEYS, 0) to KEY, - MemberInfoExtension.GROUP_ID to "DEFAULT_MEMBER_GROUP_ID", + MemberInfoExtension.GROUP_ID to UUID.randomUUID().toString(), *convertPublicKeys().toTypedArray(), *convertEndpoints().toTypedArray(), - *additionalMemberInfoProperties.toTypedArray(), MemberInfoExtension.SOFTWARE_VERSION to "5.0.0", MemberInfoExtension.PLATFORM_VERSION to "5000", DUMMY_KEY to "dummyValue", NULL_KEY to null, + *additionalMemberContext.toTypedArray(), ), converters ) From 06d36201d79198dac7f62a5a13956c1951fbe641 Mon Sep 17 00:00:00 2001 From: Yash Nabar Date: Mon, 20 Nov 2023 15:31:41 +0000 Subject: [PATCH 3/3] Address comment --- .../net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt index ae995a971c7..0c39a7e99e7 100644 --- a/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt +++ b/libs/membership/membership-impl/src/test/kotlin/net/corda/membership/lib/impl/utils/MemberInfoTestUtils.kt @@ -61,7 +61,7 @@ fun createDummyMemberContext( sortedMapOf( MemberInfoExtension.PARTY_NAME to "O=Alice,L=London,C=GB", String.format(MemberInfoExtension.PARTY_SESSION_KEYS, 0) to KEY, - MemberInfoExtension.GROUP_ID to UUID.randomUUID().toString(), + MemberInfoExtension.GROUP_ID to UUID(0,1).toString(), *convertPublicKeys().toTypedArray(), *convertEndpoints().toTypedArray(), MemberInfoExtension.SOFTWARE_VERSION to "5.0.0",