@Test
public void mergeTypes() {

    // Build a descriptor with two copies of MyOtherRecord (essentially).
    FileDescriptor updatedDescriptor = mutateFile(fileBuilder -> {
        DescriptorProtos.DescriptorProto newMessageType = fileBuilder.getMessageTypeList().stream()
                .filter(message -> message.getName().equals("MyOtherRecord"))
                .findFirst()
                .get()
                .toBuilder()
                .setName("MyOtherOtherRecord")
                .build();
        fileBuilder.addMessageType(newMessageType);
        fileBuilder.getMessageTypeBuilderList().forEach(message -> {
            if (message.getName().equals(RecordMetaDataBuilder.DEFAULT_UNION_NAME)) {
                message.addField(DescriptorProtos.FieldDescriptorProto.newBuilder()
                        .setLabel(DescriptorProtos.FieldDescriptorProto.Label.LABEL_OPTIONAL)
                        .setType(DescriptorProtos.FieldDescriptorProto.Type.TYPE_MESSAGE)
                        .setTypeName("MyOtherOtherRecord")
                        .setName("_MyOtherOtherRecord")
                        .setNumber(message.getFieldList().stream().mapToInt(DescriptorProtos.FieldDescriptorProto::getNumber).max().orElse(0) + 1)
                );
            }
        });
    });
    assertThat(updatedDescriptor.getMessageTypes().stream().map(Descriptor::getName).collect(Collectors.toSet()),
            containsInAnyOrder("MySimpleRecord", "MyOtherRecord", "MyOtherOtherRecord", RecordMetaDataBuilder.DEFAULT_UNION_NAME));
    RecordMetaData metaData1 = RecordMetaData.build(updatedDescriptor);
    assertThat(metaData1.getRecordTypes().keySet(), containsInAnyOrder("MySimpleRecord", "MyOtherRecord", "MyOtherOtherRecord"));
    FileDescriptor secondDescriptor = mutateFile(updatedDescriptor, fileBuilder -> {
        DescriptorProtos.DescriptorProto.Builder myOtherOtherDescriptor = fileBuilder.getMessageTypeBuilderList().stream()
                .filter(message -> message.getName().equals("MyOtherOtherRecord"))
                .findFirst()
                .get();
        int index = fileBuilder.getMessageTypeBuilderList().indexOf(myOtherOtherDescriptor);
        fileBuilder.removeMessageType(index);
        fileBuilder.getMessageTypeBuilderList().forEach(message -> {
            if (message.getName().equals(RecordMetaDataBuilder.DEFAULT_UNION_NAME)) {
                message.getFieldBuilderList().forEach(field -> {
                    if (field.getTypeName().equals("MyOtherOtherRecord")) {
                        field.setTypeName("MyOtherRecord");
                    }
                });
            }
        });
    });
    RecordMetaData metaData2 = replaceRecordsDescriptor(metaData1, secondDescriptor, metaDataProtoBuilder -> {
        RecordMetaDataProto.RecordType.Builder myOtherOtherRecord = metaDataProtoBuilder.getRecordTypesBuilderList().stream()
                .filter(recordType -> recordType.getName().equals("MyOtherOtherRecord"))
                .findFirst()
                .get();
        int index = metaDataProtoBuilder.getRecordTypesBuilderList().indexOf(myOtherOtherRecord);
        metaDataProtoBuilder.removeRecordTypes(index);
    });
    assertThat(metaData2.getRecordTypes().keySet(), containsInAnyOrder("MySimpleRecord", "MyOtherRecord"));
    assertInvalid("record type corresponds to multiple types in old meta-data", metaData1, metaData2);
}