浏览代码

Ticket 47586 - CI tests: test case for 47490

Bug Description:
	It exists a some test cases for bug 47490. This bug is to
	implemented those test cases into with lib389.

Fix Description:
	The fix implements the following test cases:
		1 - Add OC_A to master
			=> schema replicated
		2 - Add OC_B to consumer
			=> schema NOT replicated
		3 - Add OC_B/OC_C to master
			=> schema replicated
		4 - Add MUST attr to OC_A on master
			=> schema replicated
		5 - Add MUST attr to OC_B on consumer and OC_D to master
			=> schema NOT replicated
		6 - Add MUST attr to OC_B on master
			=> schema replicated
		7 - Add MAY attr to OC_A on master
			=> schema replicated
		8 - Add MAY attr to OC_B on consumer and MAY attr on OC_D to master
			=> schema NOT replicated
		9 - Add MAY attr to OC_B on master
			=> schema not replicated

	It uses a py.fixture to create the topology for the module

https://fedorahosted.org/389/ticket/47586

Reviewed by: Roberto Polli, Rich Megginson (thanks you Roberto and Rich !!)

Platforms tested: F19 (jenkins)

Flag Day: no

Doc impact: no
Thierry bordaz (tbordaz) 12 年之前
父节点
当前提交
5e2553044c
共有 2 个文件被更改,包括 739 次插入0 次删除
  1. 68 0
      dirsrvtests/tickets/constants.py
  2. 671 0
      dirsrvtests/tickets/ticket47490_test.py

+ 68 - 0
dirsrvtests/tickets/constants.py

@@ -0,0 +1,68 @@
+'''
+Created on Oct 31, 2013
+
+@author: tbordaz
+'''
+import os
+from lib389 import DN_DM
+
+LOCALHOST = "localhost.localdomain"
+SUFFIX    = 'dc=example,dc=com'
+PASSWORD  = 'password'
+
+# Used for standalone topology
+HOST_STANDALONE = LOCALHOST
+PORT_STANDALONE = 33389
+SERVERID_STANDALONE = 'standalone'
+
+# Used for One master / One consumer topology
+HOST_MASTER = LOCALHOST
+PORT_MASTER = 40389
+SERVERID_MASTER = 'master'
+REPLICAID_MASTER = 1
+
+HOST_CONSUMER = LOCALHOST
+PORT_CONSUMER = 50389
+SERVERID_CONSUMER = 'consumer'
+
+# Used for two masters / two consumers toplogy
+HOST_MASTER_1 = LOCALHOST
+PORT_MASTER_1 = 44389
+SERVERID_MASTER_1 = 'master_1'
+REPLICAID_MASTER_1 = 1
+
+HOST_MASTER_2 = LOCALHOST
+PORT_MASTER_2 = 45389
+SERVERID_MASTER_2 = 'master_2'
+REPLICAID_MASTER_2 = 2
+
+HOST_CONSUMER_1 = LOCALHOST
+PORT_CONSUMER_1 = 54389
+SERVERID_CONSUMER_1 = 'consumer_1'
+
+HOST_CONSUMER_2 = LOCALHOST
+PORT_CONSUMER_2 = 55389
+SERVERID_CONSUMER_2 = 'consumer_2'
+
+args_standalone = {
+                   'prefix': os.environ.get('PREFIX', None),
+                   'backupdir': os.environ.get('BACKUPDIR', "/tmp"),
+                   'newrootdn': DN_DM,
+                   'newrootpw': PASSWORD,
+                   'newhost': HOST_STANDALONE,
+                   'newport': PORT_STANDALONE,
+                   'newinstance': SERVERID_STANDALONE,
+                   'newsuffix': SUFFIX,
+                   'no_admin': True}
+
+# This is a template
+args_instance = {
+                   'prefix': os.environ.get('PREFIX', None),
+                   'backupdir': os.environ.get('BACKUPDIR', "/tmp"),
+                   'newrootdn': DN_DM,
+                   'newrootpw': PASSWORD,
+                   'newhost': LOCALHOST,
+                   'newport': 389,
+                   'newinstance': "template",
+                   'newsuffix': SUFFIX,
+                   'no_admin': True}

+ 671 - 0
dirsrvtests/tickets/ticket47490_test.py

@@ -0,0 +1,671 @@
+'''
+Created on Nov 7, 2013
+
+@author: tbordaz
+'''
+import os
+import sys
+import time
+import ldap
+import logging
+import socket
+import time
+import logging
+import pytest
+import re
+from lib389 import DirSrv, Entry, tools
+from lib389.tools import DirSrvTools
+from lib389._constants import *
+from constants import *
+
+logging.getLogger(__name__).setLevel(logging.DEBUG)
+log = logging.getLogger(__name__)
+
+installation_prefix = None
+
+TEST_REPL_DN = "cn=test_repl, %s" % SUFFIX
+ENTRY_DN = "cn=test_entry, %s" % SUFFIX
+MUST_OLD = "(postalAddress $ preferredLocale)"
+MUST_NEW = "(postalAddress $ preferredLocale $ telexNumber)"
+MAY_OLD  = "(postalCode $ street)"
+MAY_NEW  = "(postalCode $ street $ postOfficeBox)"
+
+def _ds_create_instance(args):
+    # create the standalone instance
+    return tools.DirSrvTools.createInstance(args, verbose=False)
+
+def _ds_rebind_instance(dirsrv):
+    args_instance['prefix']      = dirsrv.prefix
+    args_instance['backupdir']   = dirsrv.backupdir
+    args_instance['newrootdn']   = dirsrv.binddn
+    args_instance['newrootpw']   = dirsrv.bindpw
+    args_instance['newhost']     = dirsrv.host
+    args_instance['newport']     = dirsrv.port
+    args_instance['newinstance'] = dirsrv.serverId
+    args_instance['newsuffix']   = SUFFIX
+    args_instance['no_admin']    = True
+
+    return tools.DirSrvTools.createInstance(args_instance)
+
+class TopologyMasterConsumer(object):
+    def __init__(self, master, consumer):
+        self.master = master
+        self.consumer = consumer
+
+def pattern_errorlog(file, log_pattern):
+    try:
+        pattern_errorlog.last_pos += 1
+    except AttributeError:
+        pattern_errorlog.last_pos = 0
+    
+    found = None
+    log.debug("_pattern_errorlog: start at offset %d" % pattern_errorlog.last_pos)
+    file.seek(pattern_errorlog.last_pos)
+    
+    # Use a while true iteration because 'for line in file: hit a
+    # python bug that break file.tell()
+    while True:
+        line = file.readline()
+        log.debug("_pattern_errorlog: [%d] %s" % (file.tell(), line))
+        found = log_pattern.search(line)
+        if ((line == '') or (found)):
+            break
+        
+    log.debug("_pattern_errorlog: end at offset %d" % file.tell())
+    pattern_errorlog.last_pos = file.tell()
+    return found
+
+def _oc_definition(oid_ext, name, must=None, may=None):
+    oid  = "1.2.3.4.5.6.7.8.9.10.%d" % oid_ext
+    desc = 'To test ticket 47490'
+    sup  = 'person'
+    if not must:
+        must = MUST_OLD
+    if not may:
+        may = MAY_OLD
+    
+    new_oc = "( %s  NAME '%s' DESC '%s' SUP %s AUXILIARY MUST %s MAY %s )" % (oid, name, desc, sup, must, may)
+    return new_oc
+
+def add_OC(instance, oid_ext, name):
+    new_oc = _oc_definition(oid_ext, name)
+    instance.addSchema('objectClasses', new_oc)
+
+def mod_OC(instance, oid_ext, name, old_must=None, old_may=None, new_must=None, new_may=None):
+    old_oc = _oc_definition(oid_ext, name, old_must, old_may)
+    new_oc = _oc_definition(oid_ext, name, new_must, new_may)
+    instance.delSchema('objectClasses', old_oc)
+    instance.addSchema('objectClasses', new_oc)
+    
+def trigger_schema_push(topology):
+    """
+        It triggers an update on the supplier. This will start a replication
+        session and a schema push
+    """
+    try:
+        trigger_schema_push.value += 1
+    except AttributeError:
+        trigger_schema_push.value = 1
+    replace = [(ldap.MOD_REPLACE, 'telephonenumber', str(trigger_schema_push.value))]
+    topology.master.modify_s(ENTRY_DN, replace)
+    
+    # wait 10 seconds that the update is replicated
+    loop = 0
+    while loop <= 10:
+        try:
+            ent = topology.consumer.getEntry(ENTRY_DN, ldap.SCOPE_BASE, "(objectclass=*)", ['telephonenumber'])
+            val = ent.telephonenumber or "0"
+            if int(val) == trigger_schema_push.value:
+                return
+            # the expected value is not yet replicated. try again
+            time.sleep(1)
+            loop += 1
+            log.debug("trigger_schema_push: receive %s (expected %d)" % (val, trigger_schema_push.value))
+        except ldap.NO_SUCH_OBJECT:
+            time.sleep(1)
+            loop += 1
+
+#@pytest.fixture(scope="module")
+def topology(request):
+    '''
+        This fixture is used to create a replicated topology for the 'module'.
+        The replicated topology is MASTER -> Consumer.
+        At the beginning, It may exists a master instance and/or a consumer instance.
+        It may also exists a backup for the master and/or the consumer.
+    
+        Principle:
+            If master instance exists:
+                restart it
+            If consumer instance exists:
+                restart it
+            If backup of master AND backup of consumer exists:
+                create or rebind to consumer
+                create or rebind to master
+
+                restore master   from backup
+                restore consumer from backup
+            else:
+                Cleanup everything
+                    remove instances
+                    remove backups
+                Create instances
+                Initialize replication
+                Create backups
+    '''
+    global installation_prefix
+
+    if installation_prefix:
+        args_instance['prefix'] = installation_prefix
+    
+    # Args for the master instance
+    args_instance['newhost'] = HOST_MASTER
+    args_instance['newport'] = PORT_MASTER
+    args_instance['newinstance'] = SERVERID_MASTER
+    args_master = args_instance.copy()
+    
+    # Args for the consumer instance
+    args_instance['newhost'] = HOST_CONSUMER
+    args_instance['newport'] = PORT_CONSUMER
+    args_instance['newinstance'] = SERVERID_CONSUMER
+    args_consumer = args_instance.copy()
+
+    
+    # Get the status of the backups
+    backup_master   = DirSrvTools.existsBackup(args_master)
+    backup_consumer = DirSrvTools.existsBackup(args_consumer)
+    
+    # Get the status of the instance and restart it if it exists
+    instance_master   = DirSrvTools.existsInstance(args_master)
+    if instance_master:
+        DirSrvTools.stop(instance_master, timeout=10)
+        DirSrvTools.start(instance_master, timeout=10)
+        
+    instance_consumer = DirSrvTools.existsInstance(args_consumer)
+    if instance_consumer:
+        DirSrvTools.stop(instance_consumer, timeout=10)
+        DirSrvTools.start(instance_consumer, timeout=10)
+    
+    if backup_master and backup_consumer:
+        # The backups exist, assuming they are correct 
+        # we just re-init the instances with them
+        master   = _ds_create_instance(args_master)
+        consumer = _ds_create_instance(args_consumer)
+        
+        # restore master from backup
+        DirSrvTools.stop(master, timeout=10)
+        DirSrvTools.instanceRestoreFS(master, backup_master)
+        DirSrvTools.start(master, timeout=10)
+        
+        # restore consumer from backup
+        DirSrvTools.stop(consumer, timeout=10)
+        DirSrvTools.instanceRestoreFS(consumer, backup_consumer)
+        DirSrvTools.start(consumer, timeout=10)
+    else:
+        # We should be here only in two conditions
+        #      - This is the first time a test involve master-consumer
+        #        so we need to create everything
+        #      - Something weird happened (instance/backup destroyed)
+        #        so we discard everything and recreate all
+        
+        # Remove all the backups. So even if we have a specific backup file
+        # (e.g backup_master) we clear all backups that an instance my have created
+        if backup_master:
+            DirSrvTools.clearInstanceBackupFS(dirsrv=instance_master)
+        if backup_consumer:
+            DirSrvTools.clearInstanceBackupFS(dirsrv=instance_consumer)
+        
+        # Remove all the instances
+        if instance_master:
+            DirSrvTools.removeInstance(instance_master)
+        if instance_consumer:
+            DirSrvTools.removeInstance(instance_consumer)
+            
+        # Create the instance
+        master   = _ds_create_instance(args_master)
+        consumer = _ds_create_instance(args_consumer)
+    
+        # 
+        # Now prepare the Master-Consumer topology
+        #
+        # First Enable replication
+        master.enableReplication(suffix=SUFFIX, role="master", replicaId=REPLICAID_MASTER)
+        consumer.enableReplication(suffix=SUFFIX, role="consumer")
+        
+        # Initialize the supplier->consumer
+        
+        repl_agreement = master.agreement.create(consumer, SUFFIX, binddn=defaultProperties[REPLICATION_BIND_DN], bindpw=defaultProperties[REPLICATION_BIND_PW])
+    
+        if not repl_agreement:
+            log.fatal("Fail to create a replica agreement")
+            sys.exit(1)
+            
+        log.debug("%s created" % repl_agreement)
+        master.agreement.init(SUFFIX, HOST_CONSUMER, PORT_CONSUMER)
+        master.waitForReplInit(repl_agreement)
+        
+        # Check replication is working fine
+        master.add_s(Entry((TEST_REPL_DN, {
+                                                'objectclass': "top person".split(),
+                                                'sn': 'test_repl',
+                                                'cn': 'test_repl'})))
+        loop = 0
+        while loop <= 10:
+            try:
+                ent = consumer.getEntry(TEST_REPL_DN, ldap.SCOPE_BASE, "(objectclass=*)")
+                break
+            except ldap.NO_SUCH_OBJECT:
+                time.sleep(1)
+                loop += 1
+                
+        # Time to create the backups
+        DirSrvTools.stop(master, timeout=10)
+        master.backupfile = DirSrvTools.instanceBackupFS(master)
+        DirSrvTools.start(master, timeout=10)
+        
+        DirSrvTools.stop(consumer, timeout=10)
+        consumer.backupfile = DirSrvTools.instanceBackupFS(consumer)
+        DirSrvTools.start(consumer, timeout=10)
+    
+    # 
+    # Here we have two instances master and consumer
+    # with replication working. Either coming from a backup recovery
+    # or from a fresh (re)init
+    # Time to return the topology
+    return TopologyMasterConsumer(master, consumer)
+
+
+def test_ticket47490_init(topology):
+    """ 
+        Initialize the test environment
+    """
+    log.debug("test_ticket47490_init topology %r (master %r, consumer %r" % (topology, topology.master, topology.consumer))
+    # the test case will check if a warning message is logged in the 
+    # error log of the supplier
+    topology.master.errorlog_file = open(topology.master.errlog, "r")
+    
+    # This entry will be used to trigger attempt of schema push
+    topology.master.add_s(Entry((ENTRY_DN, {
+                                            'objectclass': "top person".split(),
+                                            'sn': 'test_entry',
+                                            'cn': 'test_entry'})))
+    
+def test_ticket47490_one(topology):
+    """
+        Summary: Extra OC Schema is pushed - no error
+        
+        If supplier schema is a superset (one extra OC) of consumer schema, then
+        schema is pushed and there is no message in the error log
+        State at startup:
+            - supplier default schema
+            - consumer default schema
+        Final state
+            - supplier +masterNewOCA
+            - consumer +masterNewOCA
+        
+    """
+    log.debug("test_ticket47490_one topology %r (master %r, consumer %r" % (topology, topology.master, topology.consumer))
+    # update the schema of the supplier so that it is a superset of 
+    # consumer. Schema should be pushed
+    add_OC(topology.master, 2, 'masterNewOCA')
+    
+    trigger_schema_push(topology)
+    master_schema_csn = topology.master.getSchemaCSN()
+    consumer_schema_csn = topology.consumer.getSchemaCSN()
+    
+    # Check the schemaCSN was updated on the consumer
+    log.debug("test_ticket47490_one master_schema_csn=%s", master_schema_csn)
+    log.debug("ctest_ticket47490_one onsumer_schema_csn=%s", consumer_schema_csn)
+    assert master_schema_csn == consumer_schema_csn
+    
+    # Check the error log of the supplier does not contain an error
+    regex = re.compile("must not be overwritten \(set replication log for additional info\)")
+    res = pattern_errorlog(topology.master.errorlog_file, regex)
+    assert res == None
+    
+def test_ticket47490_two(topology):
+    """
+        Summary: Extra OC Schema is NOT pushed - error
+        
+        If consumer schema is a superset (one extra OC) of supplier schema, then
+        schema is not pushed and there is a message in the error log
+        State at startup 
+            - supplier +masterNewOCA
+            - consumer +masterNewOCA
+        Final state
+            - supplier +masterNewOCA +masterNewOCB
+            - consumer +masterNewOCA               +consumerNewOCA
+        
+    """
+    
+    # add this OC on consumer. Supplier will no push the schema
+    add_OC(topology.consumer, 1, 'consumerNewOCA')
+    
+    # add a new OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s)
+    time.sleep(2)
+    add_OC(topology.master, 3, 'masterNewOCB')
+    
+    # now push the scheam
+    trigger_schema_push(topology)
+    master_schema_csn = topology.master.getSchemaCSN()
+    consumer_schema_csn = topology.consumer.getSchemaCSN()
+    
+    # Check the schemaCSN was NOT updated on the consumer
+    log.debug("test_ticket47490_two master_schema_csn=%s", master_schema_csn)
+    log.debug("test_ticket47490_two consumer_schema_csn=%s", consumer_schema_csn)
+    assert master_schema_csn != consumer_schema_csn
+
+    # Check the error log of the supplier does not contain an error
+    regex = re.compile("must not be overwritten \(set replication log for additional info\)")
+    res = pattern_errorlog(topology.master.errorlog_file, regex)
+    assert res
+
+
+def test_ticket47490_three(topology):
+    """
+        Summary: Extra OC Schema is pushed - no error
+        
+        If supplier schema is again a superset (one extra OC), then
+        schema is  pushed and there is no message in the error log
+        State at startup 
+            - supplier +masterNewOCA +masterNewOCB
+            - consumer +masterNewOCA               +consumerNewOCA
+        Final state
+            - supplier +masterNewOCA +masterNewOCB +consumerNewOCA
+            - consumer +masterNewOCA +masterNewOCB +consumerNewOCA
+
+    """    
+    # Do an upate to trigger the schema push attempt
+    # add this OC on consumer. Supplier will no push the schema
+    add_OC(topology.master, 1, 'consumerNewOCA')
+    
+    # now push the scheam
+    trigger_schema_push(topology)
+    master_schema_csn = topology.master.getSchemaCSN()
+    consumer_schema_csn = topology.consumer.getSchemaCSN()
+    
+    # Check the schemaCSN was NOT updated on the consumer
+    log.debug("test_ticket47490_three master_schema_csn=%s", master_schema_csn)
+    log.debug("test_ticket47490_three consumer_schema_csn=%s", consumer_schema_csn)
+    assert master_schema_csn == consumer_schema_csn
+
+    # Check the error log of the supplier does not contain an error
+    regex = re.compile("must not be overwritten \(set replication log for additional info\)")
+    res = pattern_errorlog(topology.master.errorlog_file, regex)
+    assert res == None
+    
+def test_ticket47490_four(topology):
+    """
+        Summary: Same OC - extra MUST: Schema is pushed - no error
+        
+        If supplier schema is again a superset (OC with more MUST), then
+        schema is  pushed and there is no message in the error log
+        State at startup 
+            - supplier +masterNewOCA +masterNewOCB +consumerNewOCA
+            - consumer +masterNewOCA +masterNewOCB +consumerNewOCA
+        Final state
+            - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA
+                       +must=telexnumber
+            - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA
+                       +must=telexnumber
+                        
+    """    
+    mod_OC(topology.master, 2, 'masterNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD, new_may=MAY_OLD)
+    
+        
+    trigger_schema_push(topology)
+    master_schema_csn = topology.master.getSchemaCSN()
+    consumer_schema_csn = topology.consumer.getSchemaCSN()
+    
+    # Check the schemaCSN was updated on the consumer
+    log.debug("test_ticket47490_four master_schema_csn=%s", master_schema_csn)
+    log.debug("ctest_ticket47490_four onsumer_schema_csn=%s", consumer_schema_csn)
+    assert master_schema_csn == consumer_schema_csn
+    
+    # Check the error log of the supplier does not contain an error
+    regex = re.compile("must not be overwritten \(set replication log for additional info\)")
+    res = pattern_errorlog(topology.master.errorlog_file, regex)
+    assert res == None
+    
+def test_ticket47490_five(topology):
+    """
+        Summary: Same OC - extra MUST: Schema is NOT pushed - error
+        
+        If consumer schema is  a superset (OC with more MUST), then
+        schema is  not pushed and there is a message in the error log
+        State at startup 
+            - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA
+                       +must=telexnumber
+            - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA
+                        +must=telexnumber
+        Final state
+            - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber
+            - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA
+                       +must=telexnumber                   +must=telexnumber
+                        
+        Note: replication log is enabled to get more details
+    """    
+    # get more detail why it fails
+    topology.master.enableReplLogging()
+    
+    # add telenumber to 'consumerNewOCA' on the consumer
+    mod_OC(topology.consumer, 1, 'consumerNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD, new_may=MAY_OLD)
+    # add a new OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s)
+    time.sleep(2)
+    add_OC(topology.master, 4, 'masterNewOCC')
+        
+    trigger_schema_push(topology)
+    master_schema_csn = topology.master.getSchemaCSN()
+    consumer_schema_csn = topology.consumer.getSchemaCSN()
+    
+    # Check the schemaCSN was NOT updated on the consumer
+    log.debug("test_ticket47490_five master_schema_csn=%s", master_schema_csn)
+    log.debug("ctest_ticket47490_five onsumer_schema_csn=%s", consumer_schema_csn)
+    assert master_schema_csn != consumer_schema_csn
+    
+    #Check that replication logging display additional message about 'telexNumber' not being
+    # required in the master schema
+    # This message appears before 'must not be overwritten' so it should be check first
+    regex = re.compile("Attribute telexNumber is not required in 'consumerNewOCA' of the local supplier schema")
+    res = pattern_errorlog(topology.master.errorlog_file, regex)
+    assert res != None
+    
+    # Check the error log of the supplier does not contain an error
+    regex = re.compile("must not be overwritten \(set replication log for additional info\)")
+    res = pattern_errorlog(topology.master.errorlog_file, regex)
+    assert res != None
+
+def test_ticket47490_six(topology):
+    """
+        Summary: Same OC - extra MUST: Schema is pushed - no error
+        
+        If supplier schema is  again a superset (OC with more MUST), then
+        schema is  pushed and there is no message in the error log
+        State at startup 
+            - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber
+            - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA
+                       +must=telexnumber                   +must=telexnumber
+        Final state
+                       
+            - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+            - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+                        
+        Note: replication log is enabled to get more details
+    """    
+
+    
+    # add telenumber to 'consumerNewOCA' on the consumer
+    mod_OC(topology.master, 1, 'consumerNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD, new_may=MAY_OLD)
+        
+    trigger_schema_push(topology)
+    master_schema_csn = topology.master.getSchemaCSN()
+    consumer_schema_csn = topology.consumer.getSchemaCSN()
+    
+    # Check the schemaCSN was NOT updated on the consumer
+    log.debug("test_ticket47490_six master_schema_csn=%s", master_schema_csn)
+    log.debug("ctest_ticket47490_six onsumer_schema_csn=%s", consumer_schema_csn)
+    assert master_schema_csn == consumer_schema_csn
+    
+    # Check the error log of the supplier does not contain an error
+    regex = re.compile("must not be overwritten \(set replication log for additional info\)")
+    res = pattern_errorlog(topology.master.errorlog_file, regex)
+    assert res == None
+
+
+
+
+def test_ticket47490_seven(topology):
+    """
+        Summary: Same OC - extra MAY: Schema is pushed - no error
+        
+        If supplier schema is again a superset (OC with more MAY), then
+        schema is  pushed and there is no message in the error log
+        State at startup
+            - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+            - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+        Final stat
+            - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+                       +may=postOfficeBox
+            - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+                       +may=postOfficeBox                          
+    """    
+    mod_OC(topology.master, 2, 'masterNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD, new_may=MAY_NEW)
+    
+        
+    trigger_schema_push(topology)
+    master_schema_csn = topology.master.getSchemaCSN()
+    consumer_schema_csn = topology.consumer.getSchemaCSN()
+    
+    # Check the schemaCSN was updated on the consumer
+    log.debug("test_ticket47490_seven master_schema_csn=%s", master_schema_csn)
+    log.debug("ctest_ticket47490_seven consumer_schema_csn=%s", consumer_schema_csn)
+    assert master_schema_csn == consumer_schema_csn
+    
+    # Check the error log of the supplier does not contain an error
+    regex = re.compile("must not be overwritten \(set replication log for additional info\)")
+    res = pattern_errorlog(topology.master.errorlog_file, regex)
+    assert res == None
+    
+
+def test_ticket47490_eight(topology):
+    """
+        Summary: Same OC - extra MAY: Schema is NOT pushed - error
+        
+        If consumer schema is a superset (OC with more MAY), then
+        schema is  not pushed and there is  message in the error log
+        State at startup
+            - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+                       +may=postOfficeBox
+            - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+                       +may=postOfficeBox
+        Final state
+            - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+                       +may=postOfficeBox                                     +may=postOfficeBox
+            - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+                       +may=postOfficeBox                  +may=postOfficeBox    
+    """    
+    mod_OC(topology.consumer, 1, 'consumerNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD, new_may=MAY_NEW)
+
+    # modify OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s)
+    time.sleep(2)
+    mod_OC(topology.master, 4, 'masterNewOCC', old_must=MUST_OLD, new_must=MUST_OLD, old_may=MAY_OLD, new_may=MAY_NEW)
+        
+    trigger_schema_push(topology)
+    master_schema_csn = topology.master.getSchemaCSN()
+    consumer_schema_csn = topology.consumer.getSchemaCSN()
+    
+    # Check the schemaCSN was not updated on the consumer
+    log.debug("test_ticket47490_eight master_schema_csn=%s", master_schema_csn)
+    log.debug("ctest_ticket47490_eight onsumer_schema_csn=%s", consumer_schema_csn)
+    assert master_schema_csn != consumer_schema_csn
+    
+    #Check that replication logging display additional message about 'postOfficeBox' not being
+    # allowed in the master schema
+    # This message appears before 'must not be overwritten' so it should be check first
+    regex = re.compile("Attribute postOfficeBox is not allowed in 'consumerNewOCA' of the local supplier schema")
+    res = pattern_errorlog(topology.master.errorlog_file, regex)
+    assert res != None
+    
+    # Check the error log of the supplier does not contain an error
+    regex = re.compile("must not be overwritten \(set replication log for additional info\)")
+    res = pattern_errorlog(topology.master.errorlog_file, regex)
+    assert res != None
+    
+def test_ticket47490_nine(topology):
+    """
+        Summary: Same OC - extra MAY: Schema is pushed - no error
+        
+        If consumer schema is a superset (OC with more MAY), then
+        schema is  not pushed and there is  message in the error log
+        State at startup
+            - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+                       +may=postOfficeBox                                     +may=postOfficeBox
+            - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+                       +may=postOfficeBox                  +may=postOfficeBox 
+
+        Final state
+   
+            - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+                       +may=postOfficeBox                  +may=postOfficeBox +may=postOfficeBox
+            - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
+                       +must=telexnumber                   +must=telexnumber
+                       +may=postOfficeBox                  +may=postOfficeBox +may=postOfficeBox
+    """    
+    mod_OC(topology.master, 1, 'consumerNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD, new_may=MAY_NEW)
+        
+    trigger_schema_push(topology)
+    master_schema_csn = topology.master.getSchemaCSN()
+    consumer_schema_csn = topology.consumer.getSchemaCSN()
+    
+    # Check the schemaCSN was updated on the consumer
+    log.debug("test_ticket47490_nine master_schema_csn=%s", master_schema_csn)
+    log.debug("ctest_ticket47490_nine onsumer_schema_csn=%s", consumer_schema_csn)
+    assert master_schema_csn == consumer_schema_csn
+    
+    # Check the error log of the supplier does not contain an error
+    regex = re.compile("must not be overwritten \(set replication log for additional info\)")
+    res = pattern_errorlog(topology.master.errorlog_file, regex)
+    assert res == None
+    
+
+def run_isolated():
+    '''
+        run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
+        To run isolated without py.test, you need to 
+            - edit this file and comment '@pytest.fixture' line before 'topology' function.
+            - set the installation prefix
+            - run this program
+    '''
+    global installation_prefix
+    installation_prefix =  None
+        
+    topo = topology(True)
+    test_ticket47490_init(topo)
+    test_ticket47490_one(topo)
+    test_ticket47490_two(topo)
+    test_ticket47490_three(topo)
+    test_ticket47490_four(topo)
+    test_ticket47490_five(topo)
+    test_ticket47490_six(topo)
+    test_ticket47490_seven(topo)
+    test_ticket47490_eight(topo)
+    test_ticket47490_nine(topo)
+
+
+if __name__ == '__main__':
+    run_isolated()
+