From 6fc736a5f5d8cdf0a004698a179ee0d2ce5a91be Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Mon, 27 Nov 2017 19:21:27 +0000 Subject: [PATCH] CORDA-553 - Enable Enum Evolution --- .../serialization/amqp/AMQPSerializer.kt | 2 +- .../amqp/EnumEvolutionSerializer.kt | 89 +++++ .../serialization/amqp/SerializerFactory.kt | 2 +- .../serialization/amqp/TansformTypes.kt | 50 +++ .../serialization/amqp/TransformsSchema.kt | 104 +++--- .../amqp/EnumEvolvabilityTests.kt | 124 ++++++- .../serialization/amqp/EnumEvolveTests.kt | 323 +++++++++++++++++- .../serialization/amqp/EvolvabilityTests.kt | 4 +- ...volveTests.deserialiseNewerSetToUnknown2.C | Bin 0 -> 873 bytes ...volveTests.deserialiseNewerSetToUnknown2.D | Bin 0 -> 873 bytes ...volveTests.deserialiseNewerSetToUnknown2.E | Bin 0 -> 873 bytes ...EnumEvolveTests.deserialiseNewerWithNoRule | Bin 0 -> 670 bytes ...EnumEvolveTests.deserializeWithRename.1.AA | Bin 0 -> 763 bytes .../EnumEvolveTests.deserializeWithRename.1.B | Bin 0 -> 762 bytes .../EnumEvolveTests.deserializeWithRename.1.C | Bin 0 -> 762 bytes ...EnumEvolveTests.deserializeWithRename.2.AA | Bin 0 -> 792 bytes ...EnumEvolveTests.deserializeWithRename.2.BB | Bin 0 -> 792 bytes .../EnumEvolveTests.deserializeWithRename.2.C | Bin 0 -> 791 bytes ...EnumEvolveTests.deserializeWithRename.3.AA | Bin 0 -> 821 bytes .../EnumEvolveTests.deserializeWithRename.3.C | Bin 0 -> 820 bytes ...EnumEvolveTests.deserializeWithRename.3.XX | Bin 0 -> 821 bytes .../amqp/EnumEvolveTests.multiOperations.1.A | Bin 0 -> 760 bytes .../amqp/EnumEvolveTests.multiOperations.1.B | Bin 0 -> 760 bytes .../amqp/EnumEvolveTests.multiOperations.1.C | Bin 0 -> 760 bytes .../amqp/EnumEvolveTests.multiOperations.1.D | Bin 0 -> 760 bytes .../amqp/EnumEvolveTests.multiOperations.2.A | Bin 0 -> 811 bytes .../amqp/EnumEvolveTests.multiOperations.2.B | Bin 0 -> 811 bytes .../amqp/EnumEvolveTests.multiOperations.2.C | Bin 0 -> 811 bytes .../amqp/EnumEvolveTests.multiOperations.2.D | Bin 0 -> 811 bytes .../amqp/EnumEvolveTests.multiOperations.2.E | Bin 0 -> 811 bytes .../amqp/EnumEvolveTests.multiOperations.3.A | Bin 0 -> 857 bytes .../amqp/EnumEvolveTests.multiOperations.3.B | Bin 0 -> 857 bytes .../EnumEvolveTests.multiOperations.3.BOB | Bin 0 -> 859 bytes .../amqp/EnumEvolveTests.multiOperations.3.C | Bin 0 -> 857 bytes .../amqp/EnumEvolveTests.multiOperations.3.D | Bin 0 -> 857 bytes .../amqp/EnumEvolveTests.multiOperations.4.A | Bin 0 -> 1004 bytes .../amqp/EnumEvolveTests.multiOperations.4.B | Bin 0 -> 1004 bytes .../EnumEvolveTests.multiOperations.4.BOB | Bin 0 -> 1006 bytes .../EnumEvolveTests.multiOperations.4.CAT | Bin 0 -> 1006 bytes .../amqp/EnumEvolveTests.multiOperations.4.D | Bin 0 -> 1004 bytes .../amqp/EnumEvolveTests.multiOperations.4.F | Bin 0 -> 1004 bytes .../amqp/EnumEvolveTests.multiOperations.4.G | Bin 0 -> 1004 bytes .../EnumEvolveTests.multiOperations.5.APPLE | Bin 0 -> 1115 bytes .../amqp/EnumEvolveTests.multiOperations.5.B | Bin 0 -> 1111 bytes .../EnumEvolveTests.multiOperations.5.BBB | Bin 0 -> 1113 bytes .../EnumEvolveTests.multiOperations.5.CAT | Bin 0 -> 1113 bytes .../amqp/EnumEvolveTests.multiOperations.5.D | Bin 0 -> 1111 bytes .../EnumEvolveTests.multiOperations.5.FLUMP | Bin 0 -> 1115 bytes .../amqp/EnumEvolveTests.multiOperations.5.G | Bin 0 -> 1111 bytes 49 files changed, 635 insertions(+), 63 deletions(-) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserialiseNewerSetToUnknown2.C create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserialiseNewerSetToUnknown2.D create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserialiseNewerSetToUnknown2.E create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserialiseNewerWithNoRule create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.1.AA create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.1.B create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.1.C create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.2.AA create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.2.BB create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.2.C create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.3.AA create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.3.C create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.3.XX create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.1.A create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.1.B create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.1.C create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.1.D create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.A create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.B create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.C create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.D create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.E create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.3.A create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.3.B create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.3.BOB create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.3.C create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.3.D create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.4.A create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.4.B create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.4.BOB create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.4.CAT create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.4.D create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.4.F create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.4.G create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.APPLE create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.B create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.BBB create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.CAT create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.D create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.FLUMP create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.G diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializer.kt index d4596c5ec0..e70b55d8fc 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializer.kt @@ -35,5 +35,5 @@ interface AMQPSerializer { /** * Read the given object from the input. The envelope is provided in case the schema is required. */ - fun readObject(obj: Any, schema: SerializationSchemas, input: DeserializationInput): T + fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): T } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt new file mode 100644 index 0000000000..f8579fc21e --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt @@ -0,0 +1,89 @@ +package net.corda.nodeapi.internal.serialization.amqp + +import org.apache.qpid.proton.amqp.Symbol +import org.apache.qpid.proton.codec.Data +import java.io.NotSerializableException +import java.lang.reflect.Type +import java.util.* + +/** + * @property transforms + * + */ +class EnumEvolutionSerializer( + clazz: Type, + factory: SerializerFactory, + private val conversions : Map, + private val ordinals : Map) : AMQPSerializer { + override val type: Type = clazz + override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")!! + + companion object { + fun MutableMap.mapInPlace(f : (String)->String) { + val i = this.iterator() + while(i.hasNext()) { + val curr = (i.next()) + curr.setValue(f(curr.value)) + } + } + + /** + * @param old + * @param new + */ + fun make(old: RestrictedType, + new: AMQPSerializer, + factory: SerializerFactory, + transformsFromBlob: TransformsSchema): AMQPSerializer { + + val wireTransforms = transformsFromBlob.types[old.name] + val localTransforms = TransformsSchema.get(old.name, factory) + val transforms = if (wireTransforms?.size ?: -1 > localTransforms.size) wireTransforms!! else localTransforms + + // if either of these isn't of the cast type then something has gone terribly wrong + // elsewhere in the code + @Suppress("UNCHECKED_CAST") + val defaultRules = transforms[TransformTypes.EnumDefault] as? List + @Suppress("UNCHECKED_CAST") + val renameRules = transforms[TransformTypes.Rename] as? List + + // What values exist on the enum as it exists on the class path + val localVals = new.type.asClass()!!.enumConstants.map { it.toString() } + + var conversions : MutableMap = new.type.asClass()!!.enumConstants.map { it.toString() } + .union(defaultRules?.map { it.new }?.toSet() ?: emptySet()) + .union(renameRules?.map { it.to } ?: emptySet()) + .associateBy({ it }, { it }) + .toMutableMap() + + val rules : MutableMap = mutableMapOf() + rules.putAll(defaultRules?.associateBy({ it.new }, { it.old }) ?: emptyMap()) + rules.putAll(renameRules?.associateBy({ it.to }, { it.from }) ?: emptyMap()) + + while (conversions.filter { it.value !in localVals }.isNotEmpty()) { + conversions.mapInPlace { rules[it] ?: it } + } + + var idx = 0 + return EnumEvolutionSerializer(new.type, factory, conversions, localVals.associateBy( {it}, { idx++ })) + } + } + + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any { + var enumName = (obj as List<*>)[0] as String + + if (enumName !in conversions) { + throw NotSerializableException ("No rule to evolve enum constant $type::$enumName") + } + + return type.asClass()!!.enumConstants[ordinals[conversions[enumName]]!!] + } + + override fun writeClassInfo(output: SerializationOutput) { + throw IllegalAccessException("It should be impossible to write an evolution serializer") + } + + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) { + throw IllegalAccessException("It should be impossible to write an evolution serializer") + } +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt index d310f5a6bb..764f0b5458 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt @@ -50,7 +50,7 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { return serializersByDescriptor.computeIfAbsent(typeNotation.descriptor.name!!) { when (typeNotation) { is CompositeType -> EvolutionSerializer.make(typeNotation, newSerializer as ObjectSerializer, this) - is RestrictedType -> throw NotSerializableException("Enum evolution is not currently supported") + is RestrictedType -> EnumEvolutionSerializer.make(typeNotation, newSerializer, this, transforms) } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TansformTypes.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TansformTypes.kt index 5bb6fc05d5..6fd1e4a95f 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TansformTypes.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TansformTypes.kt @@ -27,14 +27,62 @@ enum class TransformTypes(val build: (Annotation) -> Transform) : DescribedType Unknown({ UnknownTransform() }) { override fun getDescriptor(): Any = DESCRIPTOR override fun getDescribed(): Any = ordinal + override fun validate(l : List, constants: Set) { } }, EnumDefault({ a -> EnumDefaultSchemaTransform((a as CordaSerializationTransformEnumDefault).old, a.new) }) { override fun getDescriptor(): Any = DESCRIPTOR override fun getDescribed(): Any = ordinal + + /** + * Validates a list of constant additions to an enumerated types, to be valid a default (the value + * that should be used when we cannot use the new value) must refer to a constant that exists in the + * enum class as it exists now and it cannot refer to itself. + * + * @param l The list of transforms representing new constants and the mapping from that constant to an + * existing value + * @param constants The list of enum constants on the type the transforms are being applied to + */ + override fun validate(l : List, constants: Set) { + @Suppress("UNCHECKED_CAST") (l as List).forEach { + if (!constants.contains(it.old)) { + throw NotSerializableException( + "Enum extension defaults must be to a valid constant: ${it.new} -> ${it.old}. ${it.old} " + + "doesn't exist in constant set $constants") + } + + if (it.old == it.new) { + throw NotSerializableException("Enum extension ${it.new} cannot default to itself") + } + } + } }, Rename({ a -> RenameSchemaTransform((a as CordaSerializationTransformRename).from, a.to) }) { override fun getDescriptor(): Any = DESCRIPTOR override fun getDescribed(): Any = ordinal + + /** + * Validates a list of rename transforms is valid. Such a list isn't valid if we detect a cyclic chain, + * that is a constant is renamed to something that used to exist in the enum. We do this for both + * the same constant (i.e. C -> D -> C) and multiple constants (C->D, B->C) + * + * @param l The list of transforms representing the renamed constants and the mapping between their new + * and old values + * @param constants The list of enum constants on the type the transforms are being applied to + */ + override fun validate(l : List, constants: Set) { + object : Any() { + val from : MutableSet = mutableSetOf() + val to : MutableSet = mutableSetOf() }.apply { + @Suppress("UNCHECKED_CAST") (l as List).forEach { rename -> + if (rename.to in this.to || rename.from in this.from) { + throw NotSerializableException("Cyclic renames are not allowed (${rename.to})") + } + + this.to.add(rename.from) + this.from.add(rename.to) + } + } + } } // Transform used to test the unknown handler, leave this at as the final constant, uncomment // when regenerating test cases - if Java had a pre-processor this would be much neater @@ -45,6 +93,8 @@ enum class TransformTypes(val build: (Annotation) -> Transform) : DescribedType //} ; + abstract fun validate(l: List, constants: Set) + companion object : DescribedTypeConstructor { val DESCRIPTOR = AMQPDescriptorRegistry.TRANSFORM_ELEMENT_KEY.amqpDescriptor diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt index 974de134b1..a8409a64c3 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt @@ -148,6 +148,8 @@ class EnumDefaultSchemaTransform(val old: String, val new: String) : Transform() * * @property from the name at time of change of the property * @property to the new name of the property + * + * */ class RenameSchemaTransform(val from: String, val to: String) : Transform() { companion object : DescribedTypeConstructor { @@ -192,6 +194,61 @@ data class TransformsSchema(val types: Map { val DESCRIPTOR = AMQPDescriptorRegistry.TRANSFORM_SCHEMA.amqpDescriptor + /** + * Takes a class name and either returns a cached instance of the TransformSet for it or, on a cache miss, + * instantiates the transform set before inserting into the cache and returning it. + * + * @param name fully qualified class name to lookup transforms for + * @param sf the [SerializerFactory] building this transform set. Needed as each can define it's own + * class loader and this dictates which classes we can and cannot see + */ + fun get(name: String, sf: SerializerFactory) = sf.transformsCache.computeIfAbsent(name) { + val transforms = EnumMap>(TransformTypes::class.java) + try { + val clazz = sf.classloader.loadClass(name) + + supportedTransforms.forEach { transform -> + clazz.getAnnotation(transform.type)?.let { list -> + transform.getAnnotations(list).forEach { annotation -> + val t = transform.enum.build(annotation) + + // we're explicitly rejecting repeated annotations, whilst it's fine and we'd just + // ignore them it feels like a good thing to alert the user to since this is + // more than likely a typo in their code so best make it an actual error + if (transforms.computeIfAbsent(transform.enum) { mutableListOf() } + .filter { t == it } + .isNotEmpty()) { + throw NotSerializableException( + "Repeated unique transformation annotation of type ${t.name}") + } + + transforms[transform.enum]!!.add(t) + } + + transform.enum.validate( + transforms[transform.enum] ?: emptyList(), + clazz.enumConstants.map { it.toString() }.toSet()) + } + } + } catch (_: ClassNotFoundException) { + // if we can't load the class we'll end up caching an empty list which is fine as that + // list, on lookup, won't be included in the schema because it's empty + } + + transforms + } + + private fun getAndAdd( + type: String, + sf: SerializerFactory, + map: MutableMap>>) { + get(type, sf).apply { + if (isNotEmpty()) { + map[type] = this + } + } + } + /** * Prepare a schema for encoding, takes all of the types being transmitted and inspects each * one for any transform annotations. If there are any build up a set that can be @@ -200,48 +257,10 @@ data class TransformsSchema(val types: Map>>() - - schema.types.forEach { type -> - sf.transformsCache.computeIfAbsent(type.name) { - val transforms = EnumMap>(TransformTypes::class.java) - try { - val clazz = sf.classloader.loadClass(type.name) - - supportedTransforms.forEach { transform -> - clazz.getAnnotation(transform.type)?.let { list -> - transform.getAnnotations(list).forEach { annotation -> - val t = transform.enum.build(annotation) - - // we're explicitly rejecting repeated annotations, whilst it's fine and we'd just - // ignore them it feels like a good thing to alert the user to since this is - // more than likely a typo in their code so best make it an actual error - if (transforms.computeIfAbsent(transform.enum) { mutableListOf() } - .filter { t == it }.isNotEmpty()) { - throw NotSerializableException( - "Repeated unique transformation annotation of type ${t.name}") - } - - transforms[transform.enum]!!.add(t) - } - } - } - } catch (_: ClassNotFoundException) { - // if we can't load the class we'll end up caching an empty list which is fine as that - // list, on lookup, won't be included in the schema because it's empty - } - - transforms - }.apply { - if (isNotEmpty()) { - rtn[type.name] = this - } - } - } - - return TransformsSchema(rtn) - } + fun build(schema: Schema, sf: SerializerFactory) = TransformsSchema( + mutableMapOf>>().apply { + schema.types.forEach { type -> getAndAdd(type.name, sf, this) } + }) override fun getTypeClass(): Class<*> = TransformsSchema::class.java @@ -286,6 +305,7 @@ data class TransformsSchema(val types: Map(File(path.toURI()).readBytes())) + + assertEquals (DeserializeNewerSetToUnknown.C, obj.e) + } + + // Version of the class as it was serialised + // + // @CordaSerializationTransformEnumDefaults ( + // CordaSerializationTransformEnumDefault("D", "C"), + // CordaSerializationTransformEnumDefault("E", "D")) + // enum class DeserializeNewerSetToUnknown2 { A, B, C, D, E } + // + // Version of the class as it's used in the test + enum class DeserializeNewerSetToUnknown2 { A, B, C } + + @Test + fun deserialiseNewerSetToUnknown2() { + val resource = "${this.javaClass.simpleName}.${testName()}" + val sf = testDefaultFactory() + + data class C(val e: DeserializeNewerSetToUnknown2) + + // Uncomment to re-generate test files + // val so = SerializationOutput(sf) + // File(URI("$localPath/$resource.C")).writeBytes(so.serialize(C(DeserializeNewerSetToUnknown2.C)).bytes) + // File(URI("$localPath/$resource.D")).writeBytes(so.serialize(C(DeserializeNewerSetToUnknown2.D)).bytes) + // File(URI("$localPath/$resource.E")).writeBytes(so.serialize(C(DeserializeNewerSetToUnknown2.E)).bytes) + + val path1 = EvolvabilityTests::class.java.getResource("$resource.C") + val path2 = EvolvabilityTests::class.java.getResource("$resource.D") + val path3 = EvolvabilityTests::class.java.getResource("$resource.E") + + // C will just work + val obj1 = DeserializationInput(sf).deserialize(SerializedBytes(File(path1.toURI()).readBytes())) + // D will transform directly to C + val obj2 = DeserializationInput(sf).deserialize(SerializedBytes(File(path2.toURI()).readBytes())) + // E will have to transform from E -> D -> C to work, so this should exercise that part + // of the evolution code + val obj3 = DeserializationInput(sf).deserialize(SerializedBytes(File(path3.toURI()).readBytes())) + + assertEquals (DeserializeNewerSetToUnknown2.C, obj1.e) + assertEquals (DeserializeNewerSetToUnknown2.C, obj2.e) + assertEquals (DeserializeNewerSetToUnknown2.C, obj3.e) + } + + + // Version of the class as it was serialised, evolve rule purposfuly not included to + // test failure conditions + // + // enum class DeserializeNewerWithNoRule { A, B, C, D } + // + // Class as it exists for the test + enum class DeserializeNewerWithNoRule { A, B, C } + + // Lets test to see if they forgot to provide an upgrade rule + @Test + fun deserialiseNewerWithNoRule() { + val resource = "${this.javaClass.simpleName}.${testName()}" + val sf = testDefaultFactory() + + data class C(val e: DeserializeNewerWithNoRule) + + // Uncomment to re-generate test files + // val so = SerializationOutput(sf) + // File(URI("$localPath/$resource")).writeBytes(so.serialize(C(DeserializeNewerWithNoRule.D)).bytes) + + val path = EvolvabilityTests::class.java.getResource(resource) + Assertions.assertThatThrownBy { - DeserializationInput(sf).deserialize(SerializedBytes( - File(EvolvabilityTests::class.java.getResource(resource).toURI()).readBytes())) + DeserializationInput(sf).deserialize(SerializedBytes(File(path.toURI()).readBytes())) }.isInstanceOf(NotSerializableException::class.java) } + + // Version of class as it was serialized, at some point in the "future" several + // values have been renamed + // + // First Change + // A -> AA + // @CordaSerializationTransformRenames ( + // CordaSerializationTransformRename(from ="A", to = "AA") + // ) + // enum class DeserializeWithRename { AA, B, C } + // + // Second Change + // B -> BB + // @CordaSerializationTransformRenames ( + // CordaSerializationTransformRename(from = "B", to = "BB"), + // CordaSerializationTransformRename(from = "A", to = "AA") + // ) + // enum class DeserializeWithRename { AA, BB, C } + // + // Third Change + // BB -> XX + // @CordaSerializationTransformRenames ( + // CordaSerializationTransformRename(from = "B", to = "BB"), + // CordaSerializationTransformRename(from = "BB", to = "XX"), + // CordaSerializationTransformRename(from = "A", to = "AA") + // ) + // enum class DeserializeWithRename { AA, XX, C } + // + // Finally, the version we're using to test with + enum class DeserializeWithRename { A, B, C } + + @Test + fun deserializeWithRename() { + val resource = "${this.javaClass.simpleName}.${testName()}" + val sf = testDefaultFactory() + + data class C(val e: DeserializeWithRename) + + // Uncomment to re-generate test files, needs to be done in three stages + val so = SerializationOutput(sf) + // First change + // File(URI("$localPath/$resource.1.AA")).writeBytes(so.serialize(C(DeserializeWithRename.AA)).bytes) + // File(URI("$localPath/$resource.1.B")).writeBytes(so.serialize(C(DeserializeWithRename.B)).bytes) + // File(URI("$localPath/$resource.1.C")).writeBytes(so.serialize(C(DeserializeWithRename.C)).bytes) + // Second change + // File(URI("$localPath/$resource.2.AA")).writeBytes(so.serialize(C(DeserializeWithRename.AA)).bytes) + // File(URI("$localPath/$resource.2.BB")).writeBytes(so.serialize(C(DeserializeWithRename.BB)).bytes) + // File(URI("$localPath/$resource.2.C")).writeBytes(so.serialize(C(DeserializeWithRename.C)).bytes) + // Third change + // File(URI("$localPath/$resource.3.AA")).writeBytes(so.serialize(C(DeserializeWithRename.AA)).bytes) + // File(URI("$localPath/$resource.3.XX")).writeBytes(so.serialize(C(DeserializeWithRename.XX)).bytes) + // File(URI("$localPath/$resource.3.C")).writeBytes(so.serialize(C(DeserializeWithRename.C)).bytes) + + // + // Test we can deserialize instances of the class after its first transformation + // + val path1_AA = EvolvabilityTests::class.java.getResource("$resource.1.AA") + val path1_B = EvolvabilityTests::class.java.getResource("$resource.1.B") + val path1_C = EvolvabilityTests::class.java.getResource("$resource.1.C") + + val obj1_AA = DeserializationInput(sf).deserialize(SerializedBytes(File(path1_AA.toURI()).readBytes())) + val obj1_B = DeserializationInput(sf).deserialize(SerializedBytes(File(path1_B.toURI()).readBytes())) + val obj1_C = DeserializationInput(sf).deserialize(SerializedBytes(File(path1_C.toURI()).readBytes())) + + assertEquals(DeserializeWithRename.A, obj1_AA.e) + assertEquals(DeserializeWithRename.B, obj1_B.e) + assertEquals(DeserializeWithRename.C, obj1_C.e) + + // + // Test we can deserialize instances of the class after its second transformation + // + val path2_AA = EvolvabilityTests::class.java.getResource("$resource.2.AA") + val path2_BB = EvolvabilityTests::class.java.getResource("$resource.2.BB") + val path2_C = EvolvabilityTests::class.java.getResource("$resource.2.C") + + val obj2_AA = DeserializationInput(sf).deserialize(SerializedBytes(File(path2_AA.toURI()).readBytes())) + val obj2_BB = DeserializationInput(sf).deserialize(SerializedBytes(File(path2_BB.toURI()).readBytes())) + val obj2_C = DeserializationInput(sf).deserialize(SerializedBytes(File(path2_C.toURI()).readBytes())) + + assertEquals(DeserializeWithRename.A, obj2_AA.e) + assertEquals(DeserializeWithRename.B, obj2_BB.e) + assertEquals(DeserializeWithRename.C, obj2_C.e) + + // + // Test we can deserialize instances of the class after its third transformation + // + val path3_AA = EvolvabilityTests::class.java.getResource("$resource.3.AA") + val path3_XX = EvolvabilityTests::class.java.getResource("$resource.3.XX") + val path3_C = EvolvabilityTests::class.java.getResource("$resource.3.C") + + val obj3_AA = DeserializationInput(sf).deserialize(SerializedBytes(File(path3_AA.toURI()).readBytes())) + val obj3_XX = DeserializationInput(sf).deserialize(SerializedBytes(File(path3_XX.toURI()).readBytes())) + val obj3_C = DeserializationInput(sf).deserialize(SerializedBytes(File(path3_C.toURI()).readBytes())) + + assertEquals(DeserializeWithRename.A, obj3_AA.e) + assertEquals(DeserializeWithRename.B, obj3_XX.e) + assertEquals(DeserializeWithRename.C, obj3_C.e) + } + + // The origional version of the enum, what we'll be eventually deserialising into + // enum class MultiOperations { A, B, C } + // + // First alteration, add D + // @CordaSerializationTransformEnumDefault(old = "C", new = "D") + // enum class MultiOperations { A, B, C, D } + // + // Second, add E + // @CordaSerializationTransformEnumDefaults( + // CordaSerializationTransformEnumDefault(old = "C", new = "D"), + // CordaSerializationTransformEnumDefault(old = "D", new = "E") + // ) + // enum class MultiOperations { A, B, C, D, E } + // + // Third, Rename E to BOB + // @CordaSerializationTransformEnumDefaults( + // CordaSerializationTransformEnumDefault(old = "C", new = "D"), + // CordaSerializationTransformEnumDefault(old = "D", new = "E") + // ) + // @CordaSerializationTransformRename(to = "BOB", from = "E") + // enum class MultiOperations { A, B, C, D, BOB } + // + // Fourth, Rename C to CAT, ADD F and G + // @CordaSerializationTransformEnumDefaults( + // CordaSerializationTransformEnumDefault(old = "F", new = "G"), + // CordaSerializationTransformEnumDefault(old = "BOB", new = "F"), + // CordaSerializationTransformEnumDefault(old = "D", new = "E"), + // CordaSerializationTransformEnumDefault(old = "C", new = "D") + // ) + // @CordaSerializationTransformRenames ( + // CordaSerializationTransformRename(to = "CAT", from = "C"), + // CordaSerializationTransformRename(to = "BOB", from = "E") + // ) + // enum class MultiOperations { A, B, CAT, D, BOB, F, G} + // + // Fifth, Rename F to FLUMP, Rename BOB to BBB, Rename A to APPLE + // @CordaSerializationTransformEnumDefaults( + // CordaSerializationTransformEnumDefault(old = "F", new = "G"), + // CordaSerializationTransformEnumDefault(old = "BOB", new = "F"), + // CordaSerializationTransformEnumDefault(old = "D", new = "E"), + // CordaSerializationTransformEnumDefault(old = "C", new = "D") + // ) + // @CordaSerializationTransformRenames ( + // CordaSerializationTransformRename(to = "APPLE", from = "A"), + // CordaSerializationTransformRename(to = "BBB", from = "BOB"), + // CordaSerializationTransformRename(to = "FLUMP", from = "F"), + // CordaSerializationTransformRename(to = "CAT", from = "C"), + // CordaSerializationTransformRename(to = "BOB", from = "E") + // ) + // enum class MultiOperations { APPLE, B, CAT, D, BBB, FLUMP, G} + // + // Finally, the original version of teh class that we're going to be testing with + enum class MultiOperations { A, B, C } + + @Test + fun multiOperations() { + val resource = "${this.javaClass.simpleName}.${testName()}" + val sf = testDefaultFactory() + + data class C(val e: MultiOperations) + + // Uncomment to re-generate test files, needs to be done in three stages + val so = SerializationOutput(sf) + // First change + // File(URI("$localPath/$resource.1.A")).writeBytes(so.serialize(C(MultiOperations.A)).bytes) + // File(URI("$localPath/$resource.1.B")).writeBytes(so.serialize(C(MultiOperations.B)).bytes) + // File(URI("$localPath/$resource.1.C")).writeBytes(so.serialize(C(MultiOperations.C)).bytes) + // File(URI("$localPath/$resource.1.D")).writeBytes(so.serialize(C(MultiOperations.D)).bytes) + // Second change + // File(URI("$localPath/$resource.2.A")).writeBytes(so.serialize(C(MultiOperations.A)).bytes) + // File(URI("$localPath/$resource.2.B")).writeBytes(so.serialize(C(MultiOperations.B)).bytes) + // File(URI("$localPath/$resource.2.C")).writeBytes(so.serialize(C(MultiOperations.C)).bytes) + // File(URI("$localPath/$resource.2.D")).writeBytes(so.serialize(C(MultiOperations.D)).bytes) + // File(URI("$localPath/$resource.2.E")).writeBytes(so.serialize(C(MultiOperations.E)).bytes) + // Third change + // File(URI("$localPath/$resource.3.A")).writeBytes(so.serialize(C(MultiOperations.A)).bytes) + // File(URI("$localPath/$resource.3.B")).writeBytes(so.serialize(C(MultiOperations.B)).bytes) + // File(URI("$localPath/$resource.3.C")).writeBytes(so.serialize(C(MultiOperations.C)).bytes) + // File(URI("$localPath/$resource.3.D")).writeBytes(so.serialize(C(MultiOperations.D)).bytes) + // File(URI("$localPath/$resource.3.BOB")).writeBytes(so.serialize(C(MultiOperations.BOB)).bytes) + // Fourth change + // File(URI("$localPath/$resource.4.A")).writeBytes(so.serialize(C(MultiOperations.A)).bytes) + // File(URI("$localPath/$resource.4.B")).writeBytes(so.serialize(C(MultiOperations.B)).bytes) + // File(URI("$localPath/$resource.4.CAT")).writeBytes(so.serialize(C(MultiOperations.CAT)).bytes) + // File(URI("$localPath/$resource.4.D")).writeBytes(so.serialize(C(MultiOperations.D)).bytes) + // File(URI("$localPath/$resource.4.BOB")).writeBytes(so.serialize(C(MultiOperations.BOB)).bytes) + // File(URI("$localPath/$resource.4.F")).writeBytes(so.serialize(C(MultiOperations.F)).bytes) + // File(URI("$localPath/$resource.4.G")).writeBytes(so.serialize(C(MultiOperations.G)).bytes) + // Fifth change - { APPLE, B, CAT, D, BBB, FLUMP, G} + // File(URI("$localPath/$resource.5.APPLE")).writeBytes(so.serialize(C(MultiOperations.APPLE)).bytes) + // File(URI("$localPath/$resource.5.B")).writeBytes(so.serialize(C(MultiOperations.B)).bytes) + // File(URI("$localPath/$resource.5.CAT")).writeBytes(so.serialize(C(MultiOperations.CAT)).bytes) + // File(URI("$localPath/$resource.5.D")).writeBytes(so.serialize(C(MultiOperations.D)).bytes) + // File(URI("$localPath/$resource.5.BBB")).writeBytes(so.serialize(C(MultiOperations.BBB)).bytes) + // File(URI("$localPath/$resource.5.FLUMP")).writeBytes(so.serialize(C(MultiOperations.FLUMP)).bytes) + // File(URI("$localPath/$resource.5.G")).writeBytes(so.serialize(C(MultiOperations.G)).bytes) + + val stage1Resources = listOf( + Pair("$resource.1.A", MultiOperations.A), + Pair("$resource.1.B", MultiOperations.B), + Pair("$resource.1.C", MultiOperations.C), + Pair("$resource.1.D", MultiOperations.C)) + + val stage2Resources = listOf( + Pair("$resource.2.A", MultiOperations.A), + Pair("$resource.2.B", MultiOperations.B), + Pair("$resource.2.C", MultiOperations.C), + Pair("$resource.2.D", MultiOperations.C), + Pair("$resource.2.E", MultiOperations.C)) + + val stage3Resources = listOf( + Pair("$resource.3.A", MultiOperations.A), + Pair("$resource.3.B", MultiOperations.B), + Pair("$resource.3.C", MultiOperations.C), + Pair("$resource.3.D", MultiOperations.C), + Pair("$resource.3.BOB", MultiOperations.C)) + + val stage4Resources = listOf( + Pair("$resource.4.A", MultiOperations.A), + Pair("$resource.4.B", MultiOperations.B), + Pair("$resource.4.CAT", MultiOperations.C), + Pair("$resource.4.D", MultiOperations.C), + Pair("$resource.4.BOB", MultiOperations.C), + Pair("$resource.4.F", MultiOperations.C), + Pair("$resource.4.G", MultiOperations.C)) + + val stage5Resources = listOf( + Pair("$resource.5.APPLE", MultiOperations.A), + Pair("$resource.5.B", MultiOperations.B), + Pair("$resource.5.CAT", MultiOperations.C), + Pair("$resource.5.D", MultiOperations.C), + Pair("$resource.5.BBB", MultiOperations.C), + Pair("$resource.5.FLUMP", MultiOperations.C), + Pair("$resource.5.G", MultiOperations.C)) + + fun load(l: List>) = l.map { + Pair (DeserializationInput(sf).deserialize(SerializedBytes( + File(EvolvabilityTests::class.java.getResource(it.first).toURI()).readBytes())), it.second) + } + + load (stage1Resources).forEach { assertEquals(it.second, it.first.e) } + load (stage2Resources).forEach { assertEquals(it.second, it.first.e) } + load (stage3Resources).forEach { assertEquals(it.second, it.first.e) } + load (stage4Resources).forEach { assertEquals(it.second, it.first.e) } + load (stage5Resources).forEach { assertEquals(it.second, it.first.e) } + } } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolvabilityTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolvabilityTests.kt index 173e7fd87c..10a59e1414 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolvabilityTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolvabilityTests.kt @@ -2,6 +2,7 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.serialization.DeprecatedConstructorForDeserialization import net.corda.core.serialization.SerializedBytes +import net.corda.testing.common.internal.ProjectStructure.projectRootDir import org.junit.Test import java.io.File import java.io.NotSerializableException @@ -18,7 +19,8 @@ import kotlin.test.assertEquals // 5. Comment back out the generation code and uncomment the actual test class EvolvabilityTests { // When regenerating the test files this needs to be set to the file system location of the resource files - var localPath = "file://////corda/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp" + var localPath = projectRootDir.toUri().resolve( + "node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp") @Test fun simpleOrderSwapSameType() { diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserialiseNewerSetToUnknown2.C b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserialiseNewerSetToUnknown2.C new file mode 100644 index 0000000000000000000000000000000000000000..cced4f8de74148db910f0c9c62fb86c1a99ba837 GIT binary patch literal 873 zcmcIi-AV#M7@gVmhqB9_Agmi+(UW1yv= zv9E$vF0FEKM?q9$YU|(jQA*`+#|j>Be54UM1s)}VKxr%B=jfLrBNK^ z`*cZ)Xp>GUzoM}lcGwdOr>vB(TUh%s<$>{+hAkOEU6hi8tbkP9{LiKH?Q*upCAKWP zrWG^tGqX`S3yYNRMX^>)4(7KFTkGT6)H8SHzJL&)oCeS1_@|8hSot!2jQh4 z+E)S6xwOi`9R;Put+s%9&}Ve$VwcA(Y|h!WCdj6=6^2jZUEG$yK7sXmJIhfxzY{|Q{F3QTR+P5v>VFIcNc$9$Z0+texBVaiJVF8ad sv^9S-Hadm*)5T2Aj8YxHepN{r0p^oa}{KBvb)%27--YkW`Ed~cRok(MdF3` zK76^8x_h0dSs8Q2{Tff=Lt~9GCN`ONMgP zUUsLgXg8jw7qUC@dp(WdH(!Tbhq65k{K3_2bH5wRmg%OmH9pT#us^9-j&tCg9D#LH zIJQ?cJ?I&!s^&HV64`>$6-Qi1mJ6Dog5{hhd`~6M1fuEV3K6kR$tFuSY{CjzG#2d3 zRIrF`Sw7?PAlL&^u8wO8GIjU2i2Y;4eK{h**bgku(;wdd vu`p3?!V76}J5sQIW`3St zVs2r9o@-udu4`F-PFZS5YH>-iN=hnBQEGT*Nk&j=USe*linD_&+!p2oYD~oV(cwTg zBU}^9fjIVsjHwHQDRZ3*HrKg2I5;}NZDKocmW?LXIJhok$;m7(f!l;05V#`I;ea(W zTod~N4set%WHdk$14c8W(?Ui=G$H4Oj7D%F&VwyXH1XI$9Yhl14q-YVi-s%ci9G&3KV>@t`jV9JOxGrSL$t*5`+k_qvxZ=>^fHgB* z6Z-)UriDz7jtdzLki^(QvAmGc5KYK=A)^soi1T0z6HPpJPzRBOxI>r@$ReVR>wp0B OLN-VuSjgxIayS6udG(_J literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.1.C b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.1.C new file mode 100644 index 0000000000000000000000000000000000000000..33425ddb685acc272b5aefd28e6be315eb10e98a GIT binary patch literal 762 zcmYe!FG@*dWME)uIGO|`85kHZFfcGZ1~Ql#7AxhYmgpseR9IEHI$F9Ur)!&gmAd8_ z2Icz)_yifI+u9z`V?cU{kT<3z#b*>H$j!tl!*bba!qlq;Rt_xXmGK)*#HlYUut~hi!V9gBI z#D0K-X(5xN<3dIQBr$eiEITb^G(;0}UdU(!7venF!bB5~9n?W2A?^^S1G0!{<2oR~ PypRo&2o^Frf*cM2=aKcL literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.2.AA b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.2.AA new file mode 100644 index 0000000000000000000000000000000000000000..4f75773756defb3f45cade9196fe2f1e9d44f46a GIT binary patch literal 792 zcmYe!FG@*dWME)uIGO|`85kHZFfcHK1eh5XE9Iq@=p}dq60Deo8lQBTw&=7Qpe7;kjc?81Yr>q&?3e+ zP#YN^0~t(kN3b6F$+|EUZo6JyeoAU$L8e}2UP)?EUSf`3acWU!VoqjNVo7Fxo?c>Z zVS%1&UTLmtS$wDojyocxFjPP-#Q4$SKsF;> z6U%`(_Jxe83xg?hoeMVCxjHyFI>BvXJ8+JTCe}E(E@a8cEG~iDgdPyMBGKW14KrL5 z`vDGclrCg6KofIvTF7XKBnFInM(2f$MsOj{gA15w;!LWDG`oFgcq0sueZ{`mj^ literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.2.BB b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.2.BB new file mode 100644 index 0000000000000000000000000000000000000000..521bf9ec2fb5712e1369d09c9d110e1c35e348e4 GIT binary patch literal 792 zcmYe!FG@*dWME)uIGO|`85kHZFfcHK1eh5XE9Iq@=p}dq60Deo8lQBTw&=7Qpe7;kjcp@gb{8L6VM{Y zH&7cH9|IXoa5>fkKUo)s!fn^f%TGy7EXdT$%qvMP%1g}AD^4xSOw7rwN-W9D&(llH zEiBM;%`44yEz8d-OASdaE-6+?Nrfp&4bLpe2ujUM%uQ8sc5sE;!hArDi5NdR9LPpE zo8>?p`$ERlg~61$&IOz6Tpb)7o#4)6J8+JTCe}E(E@a8cEG~iDgdPyMBGKW14KrL5 z`vDGMlsY;tWHdk%b8=e9Xow^RjCw}rg^WgUAw%xF3q#?S>*eLAq$U<*>SgAYq!#5R=I9lt7G);pWL71XWaj7TCFT|u z=(*;V=DL>U=ai*}q!yPHtE8mD6s3k|mShB_<|XE)syI8i!d<|8K#hqQFFG8^W`t{E zIS|LbkTG>(FlDZD!R9(w2M0$dxJ_&a&au(N8VA>fEIFCQC2*V20|HkZIvlWJhHGL! zz`?YT$Yg2waiqaKMHc zu8I8s2RKR>G8&+XMMNxQG(-{uMm?kRLPjIF5a+?YOf>P?!Bj*V;tpXt5QcCr*8u_M kg=~;iu#nMdA(Im#q`3|VBIJM)3z;G!;EqMr=m<*10AE!Fs{jB1 literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.3.C b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserializeWithRename.3.C new file mode 100644 index 0000000000000000000000000000000000000000..5e90893cb41986fddc5bc11db303ce07eaa9c5ac GIT binary patch literal 820 zcmYe!FG@*dWME)uIGO|`85kHZFfcI70vXH@LYnJw%xF3q#?y>*eLAq$U<*>SgAYq!#5R=I9lt7G);pWL71XWaj7T zCFT|u=(*;V=DL>U=ai*}q!yPHtE8mD6s3k|mShB_<|XE)syI8i!fjzbpvFXu9~};4 zBb?20AdY<@W9q_S%3SAy&2_F04vtQ6=dm3)$3_!t99$Q&Yg2waiqaKMHc zu8I8s2QW$<9Tzehpov99EMzo95(7p(qw_*WBe)Rf!M#j0@!7#tL>l4_VLA|oa4y#Y k0p^8lkW{dc(P<%*6C$L!4hSOTfD)jT3U@53Mn{mB0parn+5i9m literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.1.A b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.1.A new file mode 100644 index 0000000000000000000000000000000000000000..c28d4809779174a1c05252023cecf33dea2f7a9c GIT binary patch literal 760 zcmb`F%Sr<=6ozxAH?&({Kq+p7Sgj}uh3Ryn6dj8)cv+@xl+?L(W?G~x@lAp+k}ll4 zb*WqN9ZW@1Iv@yk6AnNB$@ia}bi=qu0U`A8+)*b&@Pd%>rivVTS_@c;yHm%(#c6Bj z-imAelg>W%{Pv|y&ic3Yy2LQ&hUp6c^F{o4Fu)`t@9`wn zjO_5gZOoKTUN@CwX}UiF)r@2b*vd#v0oxhL7EsgD(&W)t@Q{=0?DLdekgKz`Ez6ah RC{KTf&2FfAd;uz;{RJ)J^H=}? literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.1.B b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.1.B new file mode 100644 index 0000000000000000000000000000000000000000..4f5e9ab81cbc80e43fdeef5fdfaae81b06e039e2 GIT binary patch literal 760 zcmb_a%Sr<=6wRH!&~E(!rMMAdwW25#rqhX1bS%o?W0|&5Qs>o~X_2nPKMDRwx^VB- zrEbN4FcnGZfFRgSxSV^Eb8gP6ESiD8b%ulF{_=Ol9CXm|5ybm$!4wcLi{6%7HF3tGoeI`Hu$ z2=A&ebgv*k3!<&XaS-+xjX35(%Hn`}IAJlT9v{<`hXJPkAi^XV`eYP(Bj&OsO)CD- zOL;qD@i!-_G)U7BGp(dpt?C zMt1n$5;LWf*OyANr1EPBsAeEbz*Yuo3fRs-wt$+3mL`wJf`^<`XUkJ|L9WhXTb3(1 RQJ!vx&2Fgrd;!X!{RJ}i^I8A^ literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.1.C b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.1.C new file mode 100644 index 0000000000000000000000000000000000000000..6fd1917bebb74f5d04da06c2a5de7470f5adec12 GIT binary patch literal 760 zcmb`F%}N6?5XUpyFSJ)*Kq+2?Sgj}uh3$5u6kUt5_;F0zD5?9^-EEPc#5W1PNP6(@ z)uUd;cQ6%6>4G5GOBnw1AAU2L>4tHS0z&BFxuaf$-~}P$O$9mhv=*=wcc+eni__N5 zy%pE`C!Kxj`Rz-aob_+(b%|ll$FKJ`#^)q*;%ImCXmsct-?iL^@)ZpMP1n#ILvi5a z2MF&fnB@xcvmn|Uj)SnrXv8rOQWgi)!wHKy_4t^kJPa`P2N5R0&?lqN8!?w9X;Sfr zUdr1Mi@!ZdrD+n)&dGJ-FT*k3pb^IQM` literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.1.D b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.1.D new file mode 100644 index 0000000000000000000000000000000000000000..2ca115091f61d9c20857ac53a92cc78618764afb GIT binary patch literal 760 zcmb`F%}N6?5XUpS{Xl#51(f1Nh}DXsP}sH`rRZ9e#gAj!MoHbT?rw|pB)&=TMbd+J zuO9U(zJsYqN*4sdUc&ItfAX8jOgD^s6c9oW&mDCl1TP2~Zz?d+(^|k%+?_fOE>2rJ z_l>yPKk4jK&u?GaIHJ2PumK>fwaNoO*mrQyvDG`hy6QVCa)k=#7}mk~As% zLoem+h{fNYq};TKe&3Yq#$TGVBmSXRBE83p-TSTMjMBIofW z)r@TOzfH`NPEIEkWl_zqE})W;8UnU5Qd7WoMzRG|wX`&OG#1?Cq%vEck_&QmHnwHC SVv5pqJ8X7C)#nRP1=?Rd9`jxR literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.A b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.A new file mode 100644 index 0000000000000000000000000000000000000000..434b43c42af94e004f5fe4dda370934f73682195 GIT binary patch literal 811 zcmb_a%T59@6z!ei5oOC47~-<=K|(YknE|@dAPhmm(lQijnCalmz`~WkQvH##aPNoc z(yg%=Q^G_O6WKJkr|0&ZlY9Lr9#cRFJv@)}i4eRXWS4beqo)JGQtYo8=fiL~oB30> z7LVH9GjG7-+VsM!*A<2>UT?xCSIe+>c^&(ss^|%i1%q47D_9cT{zk{rI`Hu=2=BVE zjEB5>%gc@t$08atI_H>+l*NJuIAJlT0l%jyj|8US-5e9K2+1-EmaNZ`G^vD(AmyDo zi@!KYrRfm!zpW0fzl3uX1JjgK!=eNx+yC>`-u6Wt$88vmf_g3NSim7N7w{xC8rkr_ zP0Ud@dDB!>#r6Co9A%(}gyRg Xmevc?ViT(8Awhd?88<(?>j2~vLyZGk literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.B b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.B new file mode 100644 index 0000000000000000000000000000000000000000..f216584cdf0704080d97b6b7d079c90cbf993ddc GIT binary patch literal 811 zcmb_a%T59@6z!ei5oOC47~-<=K|(YknE|@dAPhmm(lQijnCalmz`~WkQvH##aPNoc z(yg%=Q^G_O6WKJk=brYQlY9Lr9#cRFJv@)}i4eRXWS4beqo)JGQtYo8=fiL~oB30> z7LVH9GjG7-+VsM!*A<2>UT?xCSIe+>c^&(ss^|%i1%q47D_9a5eK0zf)`5?2L3r1N zWqNt_mX{qPjzu(Pbj~psDT@USaKd6v1Ab3a9tljtyE!Ie5t3yTELoo=X;KLnLCQOG z7JqS)O4A|ce_I_|e+lO(wlSPj!=eNx+yC>`-u6Wt$88v61@&6kv4BHlF5pROG_v7; zo0y|+^5#)d74`gF2}c>IA>lX!H6@&6pq7Nw3`8VU4Yap~Ql2t7QH^otO@AY_+yV56r4!BXt6E6#`Ea5nR& zZY>_QyJy~j$F=E&SFbA!TXnq&n_Ml!-sN@dkE)_4JQfUYbzQ-d(Ck}=V`&b2d;{TK z2b)}8z2#-cz_EzNjLtdcB4x3l0Zv%VX~6Gk$|HekcsIvHEJCu3f+g#-Buy&eB1m~> z&f>3{q|$VVVcY7^`inV7F)&RzH7rVCvi-lg+S_vx$8j5mQBbdi9RnO9a{*6M!^oQd zZDWqQ$(yF4D(d~Y5{@!bL&9-JYDzfCNG%Dc8Hq@!8fkC!Xzkeds=K+TQXiDN`R=Q- YElcZzX|V~_`_Pvup=GA|A+HUPPfmdYUH||9 literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.D b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.D new file mode 100644 index 0000000000000000000000000000000000000000..71b890bdef56632d7dd9f24f3f714cd157a523f4 GIT binary patch literal 811 zcmb_a%T59@6z!ei5oOC47~-<=K|(YknStp>gFJ$SrDZ76Fw?=AfrTr7rTQaf;oc9? zrCVb$ri6(mCbDU6&pquqC-?eMJf?sUdUzh`6Crp($S&)`Mo$NVrPyCH&WGV}HuI-$ zEgrQxXWaphYtxHvy{<59@p=)S@h(mL?*EeP+r zuuLzn-tw|-#IcCRjLtdcB4x3l0Zv%VX~6Gk$|HekcsIvHEJCu3f+g#-Buy&eB1n0A z&f+glQfWHG{BNs6>o4IP#lSqusbNt9ldb>xYHj->j^j3rMnSz6b}Zl!nG1N58jWoD z-zMg$o4k2cR7E{MSHe*SYDhTFKurlJ8OW1xnt_Ofs)6=akJgTJuR5E3D)m9Ro9n(R Y+p@G?7>`Y;o`(eO*)wi_=<5LF6I1&GU;qFB literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.E b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.2.E new file mode 100644 index 0000000000000000000000000000000000000000..cf15290a05c7320a732ff2f571a2126f75a08ed0 GIT binary patch literal 811 zcmb_a%T59@6z!ei5oOC47~-<=K|(YknE|@dAPhmm(lQijnCalmz`~WkQvH##aPNoc z(yg%=Q^G_O6WKJk=brYQlY9Lr9#cRFJv@)}i4eRXWS4beqo)JGQtYo8=fiL~oB30> z7LVH9GjG7-+VsM!*A<2>UT?xCSIe+>c^&(ss^|%i1%q47D_9apKWB6-tpgw5g7B^j z%k=WtLIBvse6x3^B#{v$Kxqv6B(a47X zZDNkP$(u(-Rn+ryB^+g-hJ@n`)Rb_Nfm#wyGZ2wbHPGJb(b{qDRd=&br9LQkbKO^E YTb9-f(_#~<=OICRZW%W}^mPF830ZXmVgLXD literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.3.A b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.3.A new file mode 100644 index 0000000000000000000000000000000000000000..26cbcc8f4ece2138fa2e3359f4e550e669740d4b GIT binary patch literal 857 zcmb_a%SyvQ6rGv0DQ#DRYZp@76k>g#K`7+WY>KsFqqv!jes1_i-xhRd0A?>UEi$J5xw5CHJ_?C=)=_zHkz@InGln;}V+an3k8bk%6o z&rCHto!m^K{+)9@Q}u3_DR7DB9FIJLshxIkKF)bEqENRB9mI9emmvJ$ z6%rn*?5#?NLR<-_F2RwfcwtK75c^6(Vh{V?Jx;x8sNmo>Qmk+mSlQIih)t3-X$3Ps z^@b6N7o4PJYL=KUu`TqMa2g{}G*#GTjY2{P|MN9i_GM|BZU_yTy~`^W&@5Wg#K`7+WY>KsFqqvtEwOc!p& zAJMH_rv)cACr3tHz^# zZmRj|j;Le|`RKj?>$4ZM;^ec3iuk^1 zL#7u8)?n)rZO%`oN3Jm62!-5rPtbqp^9aQOHEqN;iqmMYHHpe+B*X#Xv_-g2Z+Z*1 literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.3.BOB b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.3.BOB new file mode 100644 index 0000000000000000000000000000000000000000..87a4636599cc079fdec3c77f650aa529d428d2ca GIT binary patch literal 859 zcmb_a%SyvQ6rGv$VcV79+JzK1g;*bG5DH0}Y>KsFqqvtEwOc!p& zAJMH_rxhnQCX2T@%1?B-P%_ZMeB4}8!qtdqv5f?zh#|wJ+I}MO1tHsjb^+RL?wNoTP2}{xDxu3 zgzvmUqE}|GWjYY@au7NMN3QG!DTxE@$q9*F?74S1b;Cf${!JvC!NfPS(94KLk~C@h z6EAfK5sBxVq}f$X@xR12(O=e68G&M`#4f57659Wtul}MhQ&lxxXjIr+Wyu1nNlTua zq(Y;({BJi?nOzn`RaQk5Y818$kWS%t0qRn?Q-BN#cfUck&oofDC&1eD87+D9>3yEI zh6S0H2dvK4B-)ro#vqrNUxY&L+7NDj=<^lw05vSc*7Io8*{Vbg#K`7+WY>KsFqqvtEwOc!p& zAJMH_rv)cACr3tHz^# zZmRj|g#K`7+8Db|XO;&#%EG@6%58bn<9D}z5WUAPs0 zM7M687M$9kAlS`tIrrQ-_i)cBPCX0(0FTcue*u8607wQeB=EEukz5(g7)OVmnhbk| zsTQZ>n{m>+bFU|=-t964F7e#s;YoO~?Od2a(DrP#(+Zw3Ag+VH1mOp- zkmyy}Ta^xkxDv%4!HKWM?bTo2%hojA5E?Rjmsc#H*|ZY)SuQln z=6}1G#@wnHs<9fspH5+?1Q`_WlpvGB-4bL`xc3dJBPtmKKDsZ!`t${@*!lFKBED}r zkm-wnHQ2gDo3qGRS7|bSZL)()ef>)_tgwSfORw*QD(;`?yC1B|-wyOm4XR=L;C&8O4JVa4m@+5@Jbf~_psqYBn@N6 z;q>6NUmw*^dRk?2t~))|_O}(K(I667;6+glx%vPNCVh2qjQaatcUrxoyc~~#V$6Mb z>cdN4hWsX7@T`U`dCy{=9ax#H{i87=wVgPaXe|2>i>(XRdQJF`Mh62`U z7P%$c$QsVrR*0BWI)dN&CX;)Si9{mCUBsZAf>aDD2`JBFp{W=c>?FWg+QMKr0mVF3 zr{Zb?jCpMg_EJy>gBpYKFEpoeJTptq>G3VYOo|2|ck|x@Sru?G9gwSfORw*QD(;`?yC1B_-wxa~{sL3=ft^{}f#o&*O z3-`Kk)z&&hu5J`iW zaX39VHS5FrNl&Xx&ULq^I-_kxX*7rg=6F$5L!RD8{fVjekCD0Wcc;}W%FFQ>s0Ls< zJk{Z)FF}5jZg>{UlJ_junYiTnLuid`$@UNoJj;D=I52M@<3i z)QjAbWn>L!96LlzY0-k;`X-ZmktK;lq+L`nC@(-N29*?)=dsXK3=DQsV4T{*U^fND zJXNR3)f5=>+8FFDKphNf49dSypUUyfEZL{Ww+s_08i3r*js>zR;9@*xEqH0sE+%Dm z7`;H&7UG|8Yo{3BZlH$D#_|V+CiRTv7Y&{q&3;LNtOz)F20hD#7&H^h8FxOYB|d)u DK!z}6 literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.4.BOB b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.4.BOB new file mode 100644 index 0000000000000000000000000000000000000000..b741fceac9f439ca428245dc442d4307deabd70d GIT binary patch literal 1006 zcmb_b%SyvQ6wM@k*mfm|;8v;|A+%Z_RSHSkvZ;5hE~>4L5t2c9`jc%>VGdsufN5(g3E zaC&fR)Q9zxo?4xpYfewG{ViE;G)M)^@y6plYvUi^~eH|U0Eu`GGdVvWg*t~Z3HZ;6(RVBngz7{b6ZZR^HFmgkD5bM1?&J95;q zXOE!{Lll;sk&P_Phe6B<%S}mT_X}i`|I70v0s%|P5F?KP#NGeZ)t#?Pl_XhV8aZkT zSfgI#7AzyFIAdEOVoGTXe(Rh>?(-}uPx3qlG6t0tq+n1@Kn2?ErmSPIodBcM76v;B zDB{tnz@U}@BVHSW-4xWpV2?qC7wS_bnwbUr^!PTL(2@?w-RxK-D*`S>W7dL~ChcNe zW{1^OB&(_T=iAx|#R&*MXugMy=hnJouKZ4*Pd0bDQ|_xf+`yZ z&y;wo#EV~&{1)9ru{8NeW1Y#1t~Y?@&=M^d!I5j)q7O%wX|B?bn(CIe1CaaZm?vujE(BxNfLA8%VpwL2 z(eq?I5ud-U8Do6Aff^ExgwSfORw?Asv#6JF^?;2t(Sh@?Tp zIGi1v_Ugm>Nms8-&keV$I-_kxX*7rg7I4s;qEP2mjgNaL?KZMrEmTV8fv1d6_0LQlF*f$okeNVF7>ye~;6ITy? zCxj*pP*8Fw4zi6A9LJoXq{%wFUm#ojUmhnD2v|~v^h69G?)v8ug6j7fqfV&3{ROtOz)F20hD#7-+HOjJp`rW1l|& DPJS?G literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.4.F b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.4.F new file mode 100644 index 0000000000000000000000000000000000000000..b9bedc2502364bada4459213896dd518397395df GIT binary patch literal 1004 zcmb_b%SyvQ6wM@ULfe%ff?KI>gwSfORw*QD(;`?yC1B_-wxa~{GMPSbCAjl127hE+ zxYva%zrlZSQe~P}1;K8HnRCybbLZSU{U93HfaAE^rwh7r9C+q9;gxO#?qS1+NbV<$ z!^y#^RU6b!x_WthZg^eI4YyUbUMDh`v(?@|j zf+mbnT=a%6a*Pm0Q%+oLC_1}eAY1%j9;XlpSW<>q5(W@={#RFLzAjx+RE=qf)D*Bz zy%3iyqv$x}Ix%8Oix&LWH-+3wEJ-2~?V^f7X#vtOD5sztkA;S6Vz83}lhh^#yD2E) zX$DQMq`-vN!eDO!YGY7kQ0|5LR7z%M$v!>4Wtd381mtdZ%#&3C7m_h+!b_WWaav}Z z(eq?&A^!QcR*LcM25QJ`EPqgFP|sL?!Q{!&?3d)pih#v4=-VE|pfRgwSfORw*QD(;`?yC1B_-wxa~{vY9?`CAjl127hE+ zxYva%zrlZS5@niJ1;K8HnRCybbLZSU{a`q-0mpH-PZxCMIPlDI!Ykbf+{1ot2U^eboKJ&-0-@Z8*Zy=y-s8>#~Y8U$TxbZH?g$dF|zi9&a`qxc_NQN+Z34& zPjz_tOOW5B8=l3ofo$=Ad7MHZU`ZKbNf z^+H^-jH2U=>qLktEn4tf-xP8$u_TE|w2LYRr3FaCpqzqoJQftrX+i4b+g?SpJ~Upq{b(g2|Jk*)PeH6#E^K_ju8ap!}2;`0Y% CaxiWH literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.APPLE b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.APPLE new file mode 100644 index 0000000000000000000000000000000000000000..95b55e36de930ecf79bed3e3565b6439581c852e GIT binary patch literal 1115 zcmb_bO;6iE5cRI}A(9iK;@%2zL6|nB5u!r!N1T92WQ)30#cdmwV%2d%Y!bn#QvU&5 zdL8wT?1el05dH(Rfo>KEQl)aScHg|2H}huQ?GFzaF%0A9D1rY`QeT7y;iNuzHe=b8hOCu``KXqOSl_2?%GNg9v#-5xHU#z zg+kE@!?qtywJ2)Q`n@P!#RFA+6va&u0@Z+(_76B4#8#ZXKCUxxZvmOoOj7^= literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.B b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.B new file mode 100644 index 0000000000000000000000000000000000000000..f6aaf7d2ff87ef338bbd9666a6e5fddc2f78ed1c GIT binary patch literal 1111 zcmb_aO;6iE5cRI}A(B(os&^{X3&K!JqY8xN51fEVWQ!VvxGiBR7LJp~CQ&&d@ekm_ zb;KXp3wQV-{0C+O+AK6kNaeEHee-7C%$sd@`v;5|hH-YYkEdafTf->WcrlIZFDcKh zwqz_f*N3z7jp47q{?CnLZ;)>_7TfIi>Z-8FGalc|dP_UuX5hHG*?gGT;%<@@%GalH_sFIAPq)So8SWHF9h+11iun8@Zn^fgc5cRC{L6Z{#aWAU4RJf&-2!Sfu>pB6E$W}23aa+=Dv2dI;b`!w~iGKhW zt|R`)Ubw>#;Xg1Ns?D~f5)!?vcHg|2H~VIWe?zC^zz@f%ak{w9x7&rR zyrE`1>OYxpZuRER-$xIhz6)p7L2qX$j`#M|Hk{+h*X3mQW%4Wz_||5byglj!S#M6f zYYbh^d0I0{jFedCOp@KD3p260`c=o+$a}^3P>MbvR=`5VYR+`lQnxO|T^`dfyB6cG3S`Z4NmnR@Cx6_x!q=_h;bO9v1S1+}%zp!>cT1xH literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.CAT b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.CAT new file mode 100644 index 0000000000000000000000000000000000000000..27d9d766f71d37be47212de7082a1dd244c50e7b GIT binary patch literal 1113 zcmb_bO;6iE5cRI}QIZp)>YWO4L72}*s3?*@Z~|%~Ti~LqwE*?zd`hCiC zt0Ng7Tf3vBmFDQ+r@ys#>J9V#=6Z*HtJlRUdBo#iv;O8`_$hGQm8C5Feo}Md<_LLj zD43oTMN}zKRHC&9NxF=ONvtUT6d_RZR@yt}tRGu(n)5-*5-a0_m?iNqmdCx+V%;Bo z%TLd`{;-z}dBn3kTj-u8c^vflV9d!DJlj{=rdXse8P67+sHB4UQU=1I{eOM6ANS?k zw&N;|5}HEh(Tmb6%h)~?k~qti(o+kb+ng;f$||WWsuYS&7`A*Ms~TLUKwKbe8Z>ji zaUkm&D0w?THZ(}`To;o!HBiblTA$k?kPjLx)0;qQ3Yxh?zb@t5d1b$3-EFK}P*T;1 zi1W#{BHo%%m5*+RpD^4hkOnrV=oRtqN!)Y;oU)-Y{;7c0OdjbJ1rOa*`PGPutD?GA r#GDDGeV(!|2jV=eX?l3vigbN?7$>=RUklG_#l7*r{7lftbq4NVN#g!hc{kpk;v|A(hK&_syGmGjF!j8yqlV7{PW_BYkRb?)Es^N<8SO7d&7LMxzb_3wzh;tp78jOtiSdx+zlLeX(0=b4r@-_93ihn zfz%^Y=}^?6wFg1Eh!?72MR8k%K#5yv?|`#@Y{hBL2PsRej1OX##D7^H_fm^>fA=jv zJ?Z+xUNYnn&+=@(dy?dF(C33OC!6DdYosQF>A?MSDva literal 0 HcmV?d00001 diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.FLUMP b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.multiOperations.5.FLUMP new file mode 100644 index 0000000000000000000000000000000000000000..86b4ed8e6a5d0a910440392e45bb15de5edb975f GIT binary patch literal 1115 zcmb_bO>fgc5cRI}p(ZB;;!aidQsI^mB2-k#A8`U|B3r>!6}KhbwpJY{jon1$gv399 z3)c~UWG~$559xpCY#MDgrIC=@i?x39X5Q?Z+5T{R#E4-S-~N2TuVIkChEZtZ!8Cra zr#!d%lCj%)Ki%4HPd{GzZ$F%Qlf2ii_1VeZo~V%}9{-$;>IdO|;JDjcS$KNfaN_n9 zc@+x9R_9YUjEYK&q84q;Md?dCP!%hR>mmfI0V^FIaW;yrIL-MuWr>yXamCs# zf|9C6M0{WTR>U(Cs&eU?{FvcPfwZtWMX!kGcjBfS;4NDk&0z=1gPF)8*^c-D$^{r)iCfgc5cRGTM@>$s6?dwNON9$*8X-_5f7CG*iEI^vP;c9GOD!BHjon0WLgF9L zORppT$X>XYAHsiNHdNbfNhBn4S?#`gGjHb24n~s`MhwIF^5Ym!!ywm&QE~8M8s9f_ zUfKi6c+q<`+ujLhZ$1Uj4?g*yIk69~I-)yZCkFdOO=ie0JJ)({P47 zivkG~OX*P5q3s(%`UEdj#j4_06(S{W=c5zO#;Kj=C76*rjlx+L80o#aW4S!RD)}{To1@I4O*jM z`fdWGsezKW4`f$^B+v6Oxutw`gogZd`}~yQPJwi>IYqCFhxg*97vYp$jqz6nwC47cE>Q5%J(b@`sJN=C sXI-qDU>)+D4LK0^W=)I3^H!wmi^Dj{ANRHJ1Fg6}-|UiLfa{FB-(2xa9smFU literal 0 HcmV?d00001