Fixed wrong affiliation for autodetected users
Also: * Refactored record processor tests * Fixed logic of user profile update * Introduced --force-update option to read default data forcibly Resolves bug 1215128 Change-Id: I701ec0c5583885daa5480b18cce8f31b82f6a6b4
This commit is contained in:
parent
044d4f8f61
commit
c6d8118d43
|
@ -190,7 +190,7 @@ def is_project_type_valid(project_type):
|
||||||
|
|
||||||
def get_user_from_runtime_storage(user_id):
|
def get_user_from_runtime_storage(user_id):
|
||||||
runtime_storage_inst = get_vault()['runtime_storage']
|
runtime_storage_inst = get_vault()['runtime_storage']
|
||||||
return runtime_storage_inst.get_by_key('user:%s' % user_id)
|
return utils.load_user(runtime_storage_inst, user_id)
|
||||||
|
|
||||||
|
|
||||||
# Utils ---------
|
# Utils ---------
|
||||||
|
|
|
@ -28,3 +28,6 @@
|
||||||
|
|
||||||
# SSH username for gerrit review system access
|
# SSH username for gerrit review system access
|
||||||
# ssh_username = user
|
# ssh_username = user
|
||||||
|
|
||||||
|
# Forcibly read default data and update records
|
||||||
|
# force_update = False
|
||||||
|
|
|
@ -37,4 +37,6 @@ OPTS = [
|
||||||
help='SSH key for gerrit review system access'),
|
help='SSH key for gerrit review system access'),
|
||||||
cfg.StrOpt('ssh-username', default='user',
|
cfg.StrOpt('ssh-username', default='user',
|
||||||
help='SSH username for gerrit review system access'),
|
help='SSH username for gerrit review system access'),
|
||||||
|
cfg.BoolOpt('force-update', default=False,
|
||||||
|
help='Forcibly read default data and update records'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -21,6 +21,7 @@ from github import MainClass
|
||||||
from stackalytics.openstack.common import log as logging
|
from stackalytics.openstack.common import log as logging
|
||||||
from stackalytics.processor import normalizer
|
from stackalytics.processor import normalizer
|
||||||
from stackalytics.processor import record_processor
|
from stackalytics.processor import record_processor
|
||||||
|
from stackalytics.processor import utils
|
||||||
from stackalytics.processor import vcs
|
from stackalytics.processor import vcs
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -84,7 +85,7 @@ def _retrieve_project_list(runtime_storage_inst, project_sources):
|
||||||
def _process_users(runtime_storage_inst, users):
|
def _process_users(runtime_storage_inst, users):
|
||||||
users_index = {}
|
users_index = {}
|
||||||
for user in users:
|
for user in users:
|
||||||
runtime_storage_inst.set_by_key('user:%s' % user['user_id'], user)
|
utils.store_user(runtime_storage_inst, user)
|
||||||
if 'user_id' in user:
|
if 'user_id' in user:
|
||||||
users_index[user['user_id']] = user
|
users_index[user['user_id']] = user
|
||||||
if 'launchpad_id' in user:
|
if 'launchpad_id' in user:
|
||||||
|
@ -119,15 +120,17 @@ def _update_default_data(runtime_storage_inst, default_data):
|
||||||
runtime_storage_inst.set_by_key(key, default_data[key])
|
runtime_storage_inst.set_by_key(key, default_data[key])
|
||||||
|
|
||||||
|
|
||||||
def process(runtime_storage_inst, default_data, sources_root):
|
def process(runtime_storage_inst, default_data, sources_root, force_update):
|
||||||
LOG.debug('Process default data')
|
LOG.debug('Process default data')
|
||||||
|
|
||||||
normalizer.normalize_default_data(default_data)
|
normalizer.normalize_default_data(default_data)
|
||||||
|
|
||||||
if _check_default_data_change(runtime_storage_inst, default_data):
|
if (_check_default_data_change(runtime_storage_inst, default_data) or
|
||||||
|
force_update):
|
||||||
|
|
||||||
_update_default_data(runtime_storage_inst, default_data)
|
_update_default_data(runtime_storage_inst, default_data)
|
||||||
|
|
||||||
|
LOG.debug('Gather release index for all repos')
|
||||||
release_index = {}
|
release_index = {}
|
||||||
for repo in runtime_storage_inst.get_by_key('repos'):
|
for repo in runtime_storage_inst.get_by_key('repos'):
|
||||||
vcs_inst = vcs.get_vcs(repo, sources_root)
|
vcs_inst = vcs.get_vcs(repo, sources_root)
|
||||||
|
@ -135,6 +138,14 @@ def process(runtime_storage_inst, default_data, sources_root):
|
||||||
|
|
||||||
record_processor_inst = record_processor.RecordProcessor(
|
record_processor_inst = record_processor.RecordProcessor(
|
||||||
runtime_storage_inst)
|
runtime_storage_inst)
|
||||||
|
# need to iterate over full view of records and generate valid
|
||||||
|
# users profiles
|
||||||
|
LOG.debug('Iterate all records to create valid users profiles')
|
||||||
|
for record in record_processor_inst.update(
|
||||||
|
runtime_storage_inst.get_all_records(), release_index):
|
||||||
|
pass
|
||||||
|
# update records according to generated users profiles
|
||||||
|
LOG.debug('Update all records according to users profiles')
|
||||||
updated_records = record_processor_inst.update(
|
updated_records = record_processor_inst.update(
|
||||||
runtime_storage_inst.get_all_records(), release_index)
|
runtime_storage_inst.get_all_records(), release_index)
|
||||||
runtime_storage_inst.set_records(updated_records)
|
runtime_storage_inst.set_records(updated_records)
|
||||||
|
|
|
@ -122,6 +122,8 @@ def update_repos(runtime_storage_inst):
|
||||||
for repo in repos:
|
for repo in repos:
|
||||||
process_repo(repo, runtime_storage_inst, record_processor_inst)
|
process_repo(repo, runtime_storage_inst, record_processor_inst)
|
||||||
|
|
||||||
|
record_processor_inst.finalize()
|
||||||
|
|
||||||
|
|
||||||
def apply_corrections(uri, runtime_storage_inst):
|
def apply_corrections(uri, runtime_storage_inst):
|
||||||
LOG.info('Applying corrections from uri %s', uri)
|
LOG.info('Applying corrections from uri %s', uri)
|
||||||
|
@ -158,7 +160,8 @@ def main():
|
||||||
return not 0
|
return not 0
|
||||||
default_data_processor.process(runtime_storage_inst,
|
default_data_processor.process(runtime_storage_inst,
|
||||||
default_data,
|
default_data,
|
||||||
cfg.CONF.sources_root)
|
cfg.CONF.sources_root,
|
||||||
|
cfg.CONF.force_update)
|
||||||
|
|
||||||
update_pids(runtime_storage_inst)
|
update_pids(runtime_storage_inst)
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ class RecordProcessor(object):
|
||||||
self.releases = runtime_storage_inst.get_by_key('releases')
|
self.releases = runtime_storage_inst.get_by_key('releases')
|
||||||
self.releases_dates = [r['end_date'] for r in self.releases]
|
self.releases_dates = [r['end_date'] for r in self.releases]
|
||||||
|
|
||||||
|
self.updated_users = set()
|
||||||
|
|
||||||
def _get_release(self, timestamp):
|
def _get_release(self, timestamp):
|
||||||
release_index = bisect.bisect(self.releases_dates, timestamp)
|
release_index = bisect.bisect(self.releases_dates, timestamp)
|
||||||
return self.releases[release_index]['release_name']
|
return self.releases[release_index]['release_name']
|
||||||
|
@ -71,9 +73,6 @@ class RecordProcessor(object):
|
||||||
LOG.debug('Create new user: %s', user)
|
LOG.debug('Create new user: %s', user)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def _store_user(self, user):
|
|
||||||
self.runtime_storage_inst.set_by_key('user:%s' % user['user_id'], user)
|
|
||||||
|
|
||||||
def _get_lp_info(self, email):
|
def _get_lp_info(self, email):
|
||||||
lp_profile = None
|
lp_profile = None
|
||||||
if not re.match(r'[\w\d_\.-]+@([\w\d_\.-]+\.)+[\w]+', email):
|
if not re.match(r'[\w\d_\.-]+@([\w\d_\.-]+\.)+[\w]+', email):
|
||||||
|
@ -88,11 +87,23 @@ class RecordProcessor(object):
|
||||||
LOG.debug('User with email %s not found', email)
|
LOG.debug('User with email %s not found', email)
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
LOG.debug('Email is mapped to launchpad user: %s', lp_profile['name'])
|
||||||
return lp_profile['name'], lp_profile['display_name']
|
return lp_profile['name'], lp_profile['display_name']
|
||||||
|
|
||||||
def _get_independent(self):
|
def _get_independent(self):
|
||||||
return self.domains_index['']
|
return self.domains_index['']
|
||||||
|
|
||||||
|
def _update_user(self, user, email):
|
||||||
|
LOG.debug('Add email %s to user %s', email, user['user_id'])
|
||||||
|
user['emails'].append(email)
|
||||||
|
company_name = self._get_company_by_email(email)
|
||||||
|
if ((company_name) and (len(user['companies']) == 1) and
|
||||||
|
(user['companies'][0]['company_name'] != company_name)):
|
||||||
|
LOG.debug('Updating affiliation of user %s to %s',
|
||||||
|
user['user_id'], company_name)
|
||||||
|
user['companies'][0]['company_name'] = company_name
|
||||||
|
self.updated_users.add(user['user_id'])
|
||||||
|
|
||||||
def _update_record_and_user(self, record):
|
def _update_record_and_user(self, record):
|
||||||
email = record['author_email'].lower()
|
email = record['author_email'].lower()
|
||||||
record['author_email'] = email
|
record['author_email'] = email
|
||||||
|
@ -102,23 +113,23 @@ class RecordProcessor(object):
|
||||||
record['launchpad_id'] = user['launchpad_id']
|
record['launchpad_id'] = user['launchpad_id']
|
||||||
else:
|
else:
|
||||||
if ('launchpad_id' in record) and (record['launchpad_id']):
|
if ('launchpad_id' in record) and (record['launchpad_id']):
|
||||||
user = self._create_user(record['launchpad_id'], email,
|
launchpad_id = record['launchpad_id']
|
||||||
record['author_name'])
|
user_name = record['author_name']
|
||||||
else:
|
else:
|
||||||
launchpad_id, user_name = self._get_lp_info(email)
|
launchpad_id, user_name = self._get_lp_info(email)
|
||||||
record['launchpad_id'] = launchpad_id
|
record['launchpad_id'] = launchpad_id
|
||||||
|
|
||||||
if (launchpad_id) and (launchpad_id in self.users_index):
|
if (launchpad_id) and (launchpad_id in self.users_index):
|
||||||
# merge emails
|
# merge emails
|
||||||
user = self.users_index[launchpad_id]
|
user = self.users_index[launchpad_id]
|
||||||
user['emails'].append(email)
|
self._update_user(user, email)
|
||||||
else:
|
else:
|
||||||
# create new
|
# create new
|
||||||
if not user_name:
|
if not user_name:
|
||||||
user_name = record['author_name']
|
user_name = record['author_name']
|
||||||
user = self._create_user(launchpad_id, email, user_name)
|
user = self._create_user(launchpad_id, email, user_name)
|
||||||
|
|
||||||
self._store_user(user)
|
utils.store_user(self.runtime_storage_inst, user)
|
||||||
self.users_index[email] = user
|
self.users_index[email] = user
|
||||||
if user['launchpad_id']:
|
if user['launchpad_id']:
|
||||||
self.users_index[user['launchpad_id']] = user
|
self.users_index[user['launchpad_id']] = user
|
||||||
|
@ -257,3 +268,19 @@ class RecordProcessor(object):
|
||||||
yield record
|
yield record
|
||||||
|
|
||||||
self.runtime_storage_inst.set_by_key('users', self.users_index)
|
self.runtime_storage_inst.set_by_key('users', self.users_index)
|
||||||
|
|
||||||
|
def _get_records_for_users_to_update(self):
|
||||||
|
for record in self.runtime_storage_inst.get_all_records():
|
||||||
|
user_id = record['user_id']
|
||||||
|
if user_id in self.updated_users:
|
||||||
|
user = self.users_index[user_id]
|
||||||
|
user_company_name = user['companies'][0]['company_name']
|
||||||
|
if record['company_name'] != user_company_name:
|
||||||
|
LOG.debug('Record company will be changed to: %s',
|
||||||
|
user_company_name)
|
||||||
|
record['company_name'] = user_company_name
|
||||||
|
yield record
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
self.runtime_storage_inst.set_records(
|
||||||
|
self._get_records_for_users_to_update())
|
||||||
|
|
|
@ -50,3 +50,11 @@ def read_json_from_uri(uri):
|
||||||
return json.loads(raw)
|
return json.loads(raw)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.warn('Error while reading uri: %s' % e)
|
LOG.warn('Error while reading uri: %s' % e)
|
||||||
|
|
||||||
|
|
||||||
|
def store_user(runtime_storage_inst, user):
|
||||||
|
runtime_storage_inst.set_by_key('user:%s' % user['user_id'], user)
|
||||||
|
|
||||||
|
|
||||||
|
def load_user(runtime_storage_inst, user_id):
|
||||||
|
return runtime_storage_inst.get_by_key('user:%s' % user_id)
|
||||||
|
|
|
@ -23,236 +23,214 @@ from stackalytics.processor import utils
|
||||||
|
|
||||||
LP_URI = 'https://api.launchpad.net/1.0/people/?ws.op=getByEmail&email=%s'
|
LP_URI = 'https://api.launchpad.net/1.0/people/?ws.op=getByEmail&email=%s'
|
||||||
|
|
||||||
|
COMPANIES = [
|
||||||
|
{
|
||||||
|
'company_name': 'SuperCompany',
|
||||||
|
'domains': ['super.com', 'super.no']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domains": ["nec.com", "nec.co.jp"],
|
||||||
|
"company_name": "NEC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'company_name': '*independent',
|
||||||
|
'domains': ['']
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
def _make_users(users):
|
USERS = [
|
||||||
users_index = {}
|
{
|
||||||
for user in users:
|
'user_id': 'john_doe',
|
||||||
if 'user_id' in user:
|
'launchpad_id': 'john_doe',
|
||||||
users_index[user['user_id']] = user
|
'user_name': 'John Doe',
|
||||||
if 'launchpad_id' in user:
|
'emails': ['johndoe@gmail.com', 'jdoe@super.no'],
|
||||||
users_index[user['launchpad_id']] = user
|
'companies': [
|
||||||
for email in user['emails']:
|
{'company_name': '*independent',
|
||||||
users_index[email] = user
|
'end_date': 1234567890},
|
||||||
return users_index
|
{'company_name': 'SuperCompany',
|
||||||
|
'end_date': 0},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
RELEASES = [
|
||||||
def _make_companies(companies):
|
{
|
||||||
domains_index = {}
|
'release_name': 'prehistory',
|
||||||
for company in companies:
|
'end_date': utils.date_to_timestamp('2011-Apr-21')
|
||||||
for domain in company['domains']:
|
},
|
||||||
domains_index[domain] = company['company_name']
|
{
|
||||||
return domains_index
|
'release_name': 'Diablo',
|
||||||
|
'end_date': utils.date_to_timestamp('2011-Sep-08')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'release_name': 'Zoo',
|
||||||
|
'end_date': utils.date_to_timestamp('2035-Sep-08')
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class TestRecordProcessor(testtools.TestCase):
|
class TestRecordProcessor(testtools.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestRecordProcessor, self).setUp()
|
super(TestRecordProcessor, self).setUp()
|
||||||
|
|
||||||
companies = [
|
|
||||||
{
|
|
||||||
'company_name': 'SuperCompany',
|
|
||||||
'domains': ['super.com', 'super.no']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"domains": ["nec.com", "nec.co.jp"],
|
|
||||||
"company_name": "NEC"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'company_name': '*independent',
|
|
||||||
'domains': ['']
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
self.user = {
|
|
||||||
'user_id': 'john_doe',
|
|
||||||
'launchpad_id': 'john_doe',
|
|
||||||
'user_name': 'John Doe',
|
|
||||||
'emails': ['johndoe@gmail.com', 'jdoe@super.no'],
|
|
||||||
'companies': [
|
|
||||||
{'company_name': '*independent',
|
|
||||||
'end_date': 1234567890},
|
|
||||||
{'company_name': 'SuperCompany',
|
|
||||||
'end_date': 0},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
self.get_users = mock.Mock(return_value=[
|
|
||||||
self.user,
|
|
||||||
])
|
|
||||||
|
|
||||||
releases = [
|
|
||||||
{
|
|
||||||
'release_name': 'prehistory',
|
|
||||||
'end_date': utils.date_to_timestamp('2011-Apr-21')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'release_name': 'Diablo',
|
|
||||||
'end_date': utils.date_to_timestamp('2011-Sep-08')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'release_name': 'Zoo',
|
|
||||||
'end_date': utils.date_to_timestamp('2035-Sep-08')
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_by_key(table):
|
|
||||||
if table == 'companies':
|
|
||||||
return _make_companies(companies)
|
|
||||||
elif table == 'users':
|
|
||||||
return _make_users(self.get_users())
|
|
||||||
elif table == 'releases':
|
|
||||||
return releases
|
|
||||||
else:
|
|
||||||
raise Exception('Wrong table %s' % table)
|
|
||||||
|
|
||||||
p_storage = mock.Mock(runtime_storage.RuntimeStorage)
|
|
||||||
p_storage.get_by_key = mock.Mock(side_effect=get_by_key)
|
|
||||||
|
|
||||||
self.runtime_storage = p_storage
|
|
||||||
self.commit_processor = record_processor.RecordProcessor(p_storage)
|
|
||||||
self.read_json_from_uri_patch = mock.patch(
|
self.read_json_from_uri_patch = mock.patch(
|
||||||
'stackalytics.processor.utils.read_json_from_uri')
|
'stackalytics.processor.utils.read_json_from_uri')
|
||||||
self.read_json = self.read_json_from_uri_patch.start()
|
self.read_launchpad = self.read_json_from_uri_patch.start()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super(TestRecordProcessor, self).tearDown()
|
super(TestRecordProcessor, self).tearDown()
|
||||||
self.read_json_from_uri_patch.stop()
|
self.read_json_from_uri_patch.stop()
|
||||||
|
|
||||||
def _generate_commits(self, email='johndoe@gmail.com', date=1999999999):
|
|
||||||
yield {
|
|
||||||
'record_type': 'commit',
|
|
||||||
'commit_id': 'de7e8f297c193fb310f22815334a54b9c76a0be1',
|
|
||||||
'author_name': 'John Doe',
|
|
||||||
'author_email': email,
|
|
||||||
'date': date,
|
|
||||||
'lines_added': 25,
|
|
||||||
'lines_deleted': 9,
|
|
||||||
'release_name': 'havana',
|
|
||||||
}
|
|
||||||
|
|
||||||
def test_get_company_by_email_mapped(self):
|
def test_get_company_by_email_mapped(self):
|
||||||
|
record_processor_inst = make_record_processor()
|
||||||
email = 'jdoe@super.no'
|
email = 'jdoe@super.no'
|
||||||
res = self.commit_processor._get_company_by_email(email)
|
res = record_processor_inst._get_company_by_email(email)
|
||||||
self.assertEquals('SuperCompany', res)
|
self.assertEquals('SuperCompany', res)
|
||||||
|
|
||||||
def test_get_company_by_email_with_long_suffix_mapped(self):
|
def test_get_company_by_email_with_long_suffix_mapped(self):
|
||||||
|
record_processor_inst = make_record_processor()
|
||||||
email = 'man@mxw.nes.nec.co.jp'
|
email = 'man@mxw.nes.nec.co.jp'
|
||||||
res = self.commit_processor._get_company_by_email(email)
|
res = record_processor_inst._get_company_by_email(email)
|
||||||
self.assertEquals('NEC', res)
|
self.assertEquals('NEC', res)
|
||||||
|
|
||||||
def test_get_company_by_email_with_long_suffix_mapped_2(self):
|
def test_get_company_by_email_with_long_suffix_mapped_2(self):
|
||||||
|
record_processor_inst = make_record_processor()
|
||||||
email = 'man@mxw.nes.nec.com'
|
email = 'man@mxw.nes.nec.com'
|
||||||
res = self.commit_processor._get_company_by_email(email)
|
res = record_processor_inst._get_company_by_email(email)
|
||||||
self.assertEquals('NEC', res)
|
self.assertEquals('NEC', res)
|
||||||
|
|
||||||
def test_get_company_by_email_not_mapped(self):
|
def test_get_company_by_email_not_mapped(self):
|
||||||
|
record_processor_inst = make_record_processor()
|
||||||
email = 'foo@boo.com'
|
email = 'foo@boo.com'
|
||||||
res = self.commit_processor._get_company_by_email(email)
|
res = record_processor_inst._get_company_by_email(email)
|
||||||
self.assertEquals(None, res)
|
self.assertEquals(None, res)
|
||||||
|
|
||||||
def test_update_commit_existing_user(self):
|
def test_update_commit_existing_user(self):
|
||||||
commit_generator = self._generate_commits()
|
record_processor_inst = make_record_processor()
|
||||||
commit = list(self.commit_processor.process(commit_generator))[0]
|
commit_generator = generate_commits()
|
||||||
|
commit = list(record_processor_inst.process(commit_generator))[0]
|
||||||
|
|
||||||
self.assertEquals('SuperCompany', commit['company_name'])
|
self.assertEquals('SuperCompany', commit['company_name'])
|
||||||
self.assertEquals('john_doe', commit['launchpad_id'])
|
self.assertEquals('john_doe', commit['launchpad_id'])
|
||||||
|
|
||||||
def test_update_commit_existing_user_old_job(self):
|
def test_update_commit_existing_user_old_job(self):
|
||||||
commit_generator = self._generate_commits(date=1000000000)
|
record_processor_inst = make_record_processor()
|
||||||
commit = list(self.commit_processor.process(commit_generator))[0]
|
commit_generator = generate_commits(date=1000000000)
|
||||||
|
commit = list(record_processor_inst.process(commit_generator))[0]
|
||||||
|
|
||||||
self.assertEquals('*independent', commit['company_name'])
|
self.assertEquals('*independent', commit['company_name'])
|
||||||
self.assertEquals('john_doe', commit['launchpad_id'])
|
self.assertEquals('john_doe', commit['launchpad_id'])
|
||||||
|
|
||||||
def test_update_commit_existing_user_new_email_known_company(self):
|
def test_update_commit_existing_user_new_email_known_company(self):
|
||||||
"""
|
# User is known to LP, his email is new to us, and maps to other
|
||||||
User is known to LP, his email is new to us, and maps to other company
|
# company. Should return other company instead of those mentioned
|
||||||
Should return other company instead of those mentioned in user db
|
# in user db
|
||||||
"""
|
|
||||||
email = 'johndoe@nec.co.jp'
|
email = 'johndoe@nec.co.jp'
|
||||||
commit_generator = self._generate_commits(email=email)
|
commit_generator = generate_commits(email=email)
|
||||||
launchpad_id = 'john_doe'
|
launchpad_id = 'john_doe'
|
||||||
self.read_json.return_value = {'name': launchpad_id,
|
self.read_launchpad.return_value = {'name': launchpad_id,
|
||||||
'display_name': launchpad_id}
|
'display_name': launchpad_id}
|
||||||
user = self.user.copy()
|
user = make_user()
|
||||||
# tell storage to return existing user
|
record_processor_inst = make_record_processor(
|
||||||
self.get_users.return_value = [user]
|
make_runtime_storage(users=[user]))
|
||||||
|
|
||||||
commit = list(self.commit_processor.process(commit_generator))[0]
|
commit = list(record_processor_inst.process(commit_generator))[0]
|
||||||
|
|
||||||
self.runtime_storage.set_by_key.assert_called_with('users', mock.ANY)
|
self.read_launchpad.assert_called_once_with(LP_URI % email)
|
||||||
self.read_json.assert_called_once_with(LP_URI % email)
|
|
||||||
self.assertIn(email, user['emails'])
|
self.assertIn(email, user['emails'])
|
||||||
self.assertEquals('NEC', commit['company_name'])
|
self.assertEquals('NEC', commit['company_name'])
|
||||||
self.assertEquals(launchpad_id, commit['launchpad_id'])
|
self.assertEquals(launchpad_id, commit['launchpad_id'])
|
||||||
|
|
||||||
def test_update_commit_existing_user_new_email_unknown_company(self):
|
def test_update_commit_existing_user_new_email_unknown_company(self):
|
||||||
"""
|
# User is known to LP, but his email is new to us. Should match
|
||||||
User is known to LP, but his email is new to us. Should match
|
# the user and return current company
|
||||||
the user and return current company
|
|
||||||
"""
|
|
||||||
email = 'johndoe@yahoo.com'
|
email = 'johndoe@yahoo.com'
|
||||||
commit_generator = self._generate_commits(email=email)
|
commit_generator = generate_commits(email=email)
|
||||||
launchpad_id = 'john_doe'
|
launchpad_id = 'john_doe'
|
||||||
self.read_json.return_value = {'name': launchpad_id,
|
self.read_launchpad.return_value = {'name': launchpad_id,
|
||||||
'display_name': launchpad_id}
|
'display_name': launchpad_id}
|
||||||
user = self.user.copy()
|
user = make_user()
|
||||||
# tell storage to return existing user
|
record_processor_inst = make_record_processor(
|
||||||
self.get_users.return_value = [user]
|
make_runtime_storage(users=[user]))
|
||||||
|
|
||||||
commit = list(self.commit_processor.process(commit_generator))[0]
|
commit = list(record_processor_inst.process(commit_generator))[0]
|
||||||
|
|
||||||
self.runtime_storage.set_by_key.assert_called_with('users', mock.ANY)
|
self.read_launchpad.assert_called_once_with(LP_URI % email)
|
||||||
self.read_json.assert_called_once_with(LP_URI % email)
|
|
||||||
self.assertIn(email, user['emails'])
|
self.assertIn(email, user['emails'])
|
||||||
self.assertEquals('SuperCompany', commit['company_name'])
|
self.assertEquals('SuperCompany', commit['company_name'])
|
||||||
self.assertEquals(launchpad_id, commit['launchpad_id'])
|
self.assertEquals(launchpad_id, commit['launchpad_id'])
|
||||||
|
|
||||||
|
def test_update_commit_existing_user_new_email_known_company_update(self):
|
||||||
|
# User is known to LP, his email is new to us and belongs to company B.
|
||||||
|
# Should match the user and return company B and update user
|
||||||
|
email = 'johndoe@nec.com'
|
||||||
|
commit_generator = generate_commits(email=email)
|
||||||
|
launchpad_id = 'john_doe'
|
||||||
|
self.read_launchpad.return_value = {'name': launchpad_id,
|
||||||
|
'display_name': launchpad_id}
|
||||||
|
user = {
|
||||||
|
'user_id': 'john_doe',
|
||||||
|
'launchpad_id': launchpad_id,
|
||||||
|
'user_name': 'John Doe',
|
||||||
|
'emails': ['johndoe@gmail.com'],
|
||||||
|
'companies': [
|
||||||
|
{'company_name': '*independent', 'end_date': 0}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
record_processor_inst = make_record_processor(
|
||||||
|
make_runtime_storage(users=[user]))
|
||||||
|
|
||||||
|
commit = list(record_processor_inst.process(commit_generator))[0]
|
||||||
|
|
||||||
|
self.read_launchpad.assert_called_once_with(LP_URI % email)
|
||||||
|
self.assertIn(email, user['emails'])
|
||||||
|
self.assertEquals('NEC', user['companies'][0]['company_name'],
|
||||||
|
message='User affiliation should be updated')
|
||||||
|
self.assertEquals('NEC', commit['company_name'])
|
||||||
|
self.assertEquals(launchpad_id, commit['launchpad_id'])
|
||||||
|
|
||||||
def test_update_commit_new_user(self):
|
def test_update_commit_new_user(self):
|
||||||
"""
|
# User is known to LP, but new to us
|
||||||
User is known to LP, but new to us
|
# Should add new user and set company depending on email
|
||||||
Should add new user and set company depending on email
|
|
||||||
"""
|
|
||||||
email = 'smith@nec.com'
|
email = 'smith@nec.com'
|
||||||
commit_generator = self._generate_commits(email=email)
|
commit_generator = generate_commits(email=email)
|
||||||
launchpad_id = 'smith'
|
launchpad_id = 'smith'
|
||||||
self.read_json.return_value = {'name': launchpad_id,
|
self.read_launchpad.return_value = {'name': launchpad_id,
|
||||||
'display_name': 'Smith'}
|
'display_name': 'Smith'}
|
||||||
self.get_users.return_value = []
|
record_processor_inst = make_record_processor(
|
||||||
|
make_runtime_storage(users=[]))
|
||||||
|
|
||||||
commit = list(self.commit_processor.process(commit_generator))[0]
|
commit = list(record_processor_inst.process(commit_generator))[0]
|
||||||
|
|
||||||
self.read_json.assert_called_once_with(LP_URI % email)
|
self.read_launchpad.assert_called_once_with(LP_URI % email)
|
||||||
self.assertEquals('NEC', commit['company_name'])
|
self.assertEquals('NEC', commit['company_name'])
|
||||||
self.assertEquals(launchpad_id, commit['launchpad_id'])
|
self.assertEquals(launchpad_id, commit['launchpad_id'])
|
||||||
|
|
||||||
def test_update_commit_new_user_unknown_to_lb(self):
|
def test_update_commit_new_user_unknown_to_lb(self):
|
||||||
"""
|
# User is new to us and not known to LP
|
||||||
User is new to us and not known to LP
|
# Should set user name and empty LPid
|
||||||
Should set user name and empty LPid
|
|
||||||
"""
|
|
||||||
email = 'inkognito@avs.com'
|
email = 'inkognito@avs.com'
|
||||||
commit_generator = self._generate_commits(email=email)
|
commit_generator = generate_commits(email=email)
|
||||||
self.read_json.return_value = None
|
self.read_launchpad.return_value = None
|
||||||
self.get_users.return_value = []
|
record_processor_inst = make_record_processor(
|
||||||
|
make_runtime_storage(users=[]))
|
||||||
|
|
||||||
commit = list(self.commit_processor.process(commit_generator))[0]
|
commit = list(record_processor_inst.process(commit_generator))[0]
|
||||||
|
|
||||||
self.read_json.assert_called_once_with(LP_URI % email)
|
self.read_launchpad.assert_called_once_with(LP_URI % email)
|
||||||
self.assertEquals('*independent', commit['company_name'])
|
self.assertEquals('*independent', commit['company_name'])
|
||||||
self.assertEquals(None, commit['launchpad_id'])
|
self.assertEquals(None, commit['launchpad_id'])
|
||||||
|
|
||||||
def test_update_commit_invalid_email(self):
|
def test_update_commit_invalid_email(self):
|
||||||
"""
|
# User's email is malformed
|
||||||
User's email is malformed
|
|
||||||
"""
|
|
||||||
email = 'error.root'
|
email = 'error.root'
|
||||||
commit_generator = self._generate_commits(email=email)
|
commit_generator = generate_commits(email=email)
|
||||||
self.read_json.return_value = None
|
self.read_launchpad.return_value = None
|
||||||
self.get_users.return_value = []
|
record_processor_inst = make_record_processor(
|
||||||
|
make_runtime_storage(users=[]))
|
||||||
|
|
||||||
commit = list(self.commit_processor.process(commit_generator))[0]
|
commit = list(record_processor_inst.process(commit_generator))[0]
|
||||||
|
|
||||||
self.assertEquals(0, self.read_json.called)
|
self.assertEquals(0, self.read_launchpad.called)
|
||||||
self.assertEquals('*independent', commit['company_name'])
|
self.assertEquals('*independent', commit['company_name'])
|
||||||
self.assertEquals(None, commit['launchpad_id'])
|
self.assertEquals(None, commit['launchpad_id'])
|
||||||
|
|
||||||
|
@ -285,8 +263,81 @@ class TestRecordProcessor(testtools.TestCase):
|
||||||
def test_update_record_no_changes(self):
|
def test_update_record_no_changes(self):
|
||||||
commit_generator = self._generate_record_commit()
|
commit_generator = self._generate_record_commit()
|
||||||
release_index = {'0afdc64bfd041b03943ceda7849c4443940b6053': 'havana'}
|
release_index = {'0afdc64bfd041b03943ceda7849c4443940b6053': 'havana'}
|
||||||
|
record_processor_inst = make_record_processor(
|
||||||
|
make_runtime_storage(users=[]))
|
||||||
|
|
||||||
updated = list(self.commit_processor.update(commit_generator,
|
updated = list(record_processor_inst.update(commit_generator,
|
||||||
release_index))
|
release_index))
|
||||||
|
|
||||||
self.assertEquals(0, len(updated))
|
self.assertEquals(0, len(updated))
|
||||||
|
|
||||||
|
|
||||||
|
# Helpers
|
||||||
|
|
||||||
|
def generate_commits(email='johndoe@gmail.com', date=1999999999):
|
||||||
|
yield {
|
||||||
|
'record_type': 'commit',
|
||||||
|
'commit_id': 'de7e8f297c193fb310f22815334a54b9c76a0be1',
|
||||||
|
'author_name': 'John Doe',
|
||||||
|
'author_email': email,
|
||||||
|
'date': date,
|
||||||
|
'lines_added': 25,
|
||||||
|
'lines_deleted': 9,
|
||||||
|
'release_name': 'havana',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def make_runtime_storage(users=None, companies=None, releases=None):
|
||||||
|
def get_by_key(table):
|
||||||
|
if table == 'companies':
|
||||||
|
return _make_companies(companies or COMPANIES)
|
||||||
|
elif table == 'users':
|
||||||
|
return _make_users(users or USERS)
|
||||||
|
elif table == 'releases':
|
||||||
|
return releases or RELEASES
|
||||||
|
else:
|
||||||
|
raise Exception('Wrong table %s' % table)
|
||||||
|
|
||||||
|
rs = mock.Mock(runtime_storage.RuntimeStorage)
|
||||||
|
rs.get_by_key = mock.Mock(side_effect=get_by_key)
|
||||||
|
return rs
|
||||||
|
|
||||||
|
|
||||||
|
def make_record_processor(runtime_storage_inst=None):
|
||||||
|
return record_processor.RecordProcessor(runtime_storage_inst or
|
||||||
|
make_runtime_storage())
|
||||||
|
|
||||||
|
|
||||||
|
def make_user():
|
||||||
|
return {
|
||||||
|
'user_id': 'john_doe',
|
||||||
|
'launchpad_id': 'john_doe',
|
||||||
|
'user_name': 'John Doe',
|
||||||
|
'emails': ['johndoe@gmail.com', 'jdoe@super.no'],
|
||||||
|
'companies': [
|
||||||
|
{'company_name': '*independent',
|
||||||
|
'end_date': 1234567890},
|
||||||
|
{'company_name': 'SuperCompany',
|
||||||
|
'end_date': 0},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _make_users(users):
|
||||||
|
users_index = {}
|
||||||
|
for user in users:
|
||||||
|
if 'user_id' in user:
|
||||||
|
users_index[user['user_id']] = user
|
||||||
|
if 'launchpad_id' in user:
|
||||||
|
users_index[user['launchpad_id']] = user
|
||||||
|
for email in user['emails']:
|
||||||
|
users_index[email] = user
|
||||||
|
return users_index
|
||||||
|
|
||||||
|
|
||||||
|
def _make_companies(companies):
|
||||||
|
domains_index = {}
|
||||||
|
for company in companies:
|
||||||
|
for domain in company['domains']:
|
||||||
|
domains_index[domain] = company['company_name']
|
||||||
|
return domains_index
|
||||||
|
|
Loading…
Reference in New Issue