Skip to content

Commit

Permalink
CORE-18414 Add MemberInfo serialization tests (#5133)
Browse files Browse the repository at this point in the history
Adds unit testing around custom AMQP serializers added for `MemberContext` and `MGMContext` as part of #5127.
Also includes some refactoring around `MemberInfo` test utilities.
  • Loading branch information
YashNabar authored Nov 24, 2023
1 parent 2130abb commit ea0b047
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 98 deletions.
3 changes: 3 additions & 0 deletions libs/membership/membership-impl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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")
Expand All @@ -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<String>
get() = memberProvidedContext.parseList(INVALID_LIST_KEY)

@Suppress("SpreadOperator")
private fun createDummyMemberInfo(): MemberInfo = MemberInfoImpl(
memberProvidedContext = LayeredPropertyMapMocks.create<MemberContextImpl>(
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<MGMContextImpl>(
sortedMapOf(
STATUS to MEMBER_STATUS_ACTIVE,
MODIFIED_TIME to modifiedTime.toString(),
DUMMY_KEY to "dummyValue",
SERIAL to "1",
),
converters
)
)

private fun convertEndpoints(): List<Pair<String, String>> {
val result = mutableListOf<Pair<String, String>>()
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<Pair<String, String>> =
ledgerKeys.mapIndexed { i, ledgerKey ->
String.format(
LEDGER_KEYS_KEY,
i
) to keyEncodingService.encodeAsString(ledgerKey)
}

private fun convertTestObjects(): List<Pair<String, String>> {
val result = mutableListOf<Pair<String, String>>()
for (i in testObjects.indices) {
Expand Down Expand Up @@ -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,
additionalMemberContext = convertTestObjects() + createInvalidListFormat(),
)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
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
import java.util.UUID

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<CustomPropertyConverter<out Any>>,
additionalMemberContext: List<Pair<String, String>> = emptyList(),
): MemberInfo = MemberInfoImpl(
memberProvidedContext = createDummyMemberContext(converters, additionalMemberContext),
mgmProvidedContext = createDummyMgmContext(converters)
)

@Suppress("SpreadOperator")
fun createDummyMemberContext(
converters: List<CustomPropertyConverter<out Any>>,
additionalMemberContext: List<Pair<String, String>> = emptyList(),
) = LayeredPropertyMapMocks.create<MemberContextImpl>(
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(0,1).toString(),
*convertPublicKeys().toTypedArray(),
*convertEndpoints().toTypedArray(),
MemberInfoExtension.SOFTWARE_VERSION to "5.0.0",
MemberInfoExtension.PLATFORM_VERSION to "5000",
DUMMY_KEY to "dummyValue",
NULL_KEY to null,
*additionalMemberContext.toTypedArray(),
), converters
)

fun createDummyMgmContext(
converters: List<CustomPropertyConverter<out Any>>,
) = LayeredPropertyMapMocks.create<MGMContextImpl>(
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<Pair<String, String>> {
val result = mutableListOf<Pair<String, String>>()
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<Pair<String, String>> = ledgerKeys.mapIndexed { i, ledgerKey ->
String.format(
MemberInfoExtension.LEDGER_KEYS_KEY, i
) to keyEncodingService.encodeAsString(ledgerKey)
}

0 comments on commit ea0b047

Please sign in to comment.