Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CORE-18414 Tests for MemberInfo serialization #5133

Merged
merged 5 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}