blob: 1bbd12c7aad682f52abb50be24cb5d3e8e09657b [file] [log] [blame]
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
"""Tests for converting internal protorpc to external protoc."""
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import copy
import difflib
import logging
import unittest
import mock
from google.protobuf import field_mask_pb2
from google.protobuf import timestamp_pb2
from api import resource_name_converters as rnc
from api.v3 import converters
from api.v3.api_proto import feature_objects_pb2
from api.v3.api_proto import issues_pb2
from api.v3.api_proto import issue_objects_pb2
from api.v3.api_proto import user_objects_pb2
from api.v3.api_proto import project_objects_pb2
from framework import authdata
from framework import exceptions
from framework import framework_constants
from framework import framework_helpers
from framework import monorailcontext
from testing import fake
from testing import testing_helpers
from tracker import field_helpers
from services import service_manager
from proto import tracker_pb2
from tracker import tracker_bizobj as tbo
EXPLICIT_DERIVATION = issue_objects_pb2.Derivation.Value('EXPLICIT')
RULE_DERIVATION = issue_objects_pb2.Derivation.Value('RULE')
Choice = project_objects_pb2.FieldDef.EnumTypeSettings.Choice
CURRENT_TIME = 12346.78
class ConverterFunctionsTest(unittest.TestCase):
def setUp(self):
self.services = service_manager.Services(
issue=fake.IssueService(),
project=fake.ProjectService(),
usergroup=fake.UserGroupService(),
user=fake.UserService(),
config=fake.ConfigService(),
template=fake.TemplateService(),
features=fake.FeaturesService())
self.cnxn = fake.MonorailConnection()
self.mc = monorailcontext.MonorailContext(self.services, cnxn=self.cnxn)
self.converter = converters.Converter(self.mc, self.services)
self.PAST_TIME = int(CURRENT_TIME - 1)
self.project_1 = self.services.project.TestAddProject(
'proj', project_id=789)
self.project_2 = self.services.project.TestAddProject(
'goose', project_id=788)
self.user_1 = self.services.user.TestAddUser('one@example.com', 111)
self.user_2 = self.services.user.TestAddUser('two@example.com', 222)
self.user_3 = self.services.user.TestAddUser('three@example.com', 333)
self.services.project.TestAddProjectMembers(
[self.user_1.user_id], self.project_1, 'CONTRIBUTOR_ROLE')
self.field_def_1_name = 'test_field_1'
self.field_def_1 = self._CreateFieldDef(
self.project_1.project_id,
self.field_def_1_name,
'STR_TYPE',
admin_ids=[self.user_1.user_id],
is_required=True,
is_multivalued=True,
is_phase_field=True,
regex='abc')
self.field_def_2_name = 'test_field_2'
self.field_def_2 = self._CreateFieldDef(
self.project_1.project_id,
self.field_def_2_name,
'INT_TYPE',
max_value=37,
is_niche=True)
self.field_def_3_name = 'days'
self.field_def_3 = self._CreateFieldDef(
self.project_1.project_id, self.field_def_3_name, 'ENUM_TYPE')
self.field_def_4_name = 'OS'
self.field_def_4 = self._CreateFieldDef(
self.project_1.project_id, self.field_def_4_name, 'ENUM_TYPE')
self.field_def_5_name = 'yellow'
self.field_def_5 = self._CreateFieldDef(
self.project_1.project_id, self.field_def_5_name, 'ENUM_TYPE')
self.field_def_7_name = 'redredred'
self.field_def_7 = self._CreateFieldDef(
self.project_1.project_id,
self.field_def_7_name,
'ENUM_TYPE',
is_restricted_field=True,
editor_ids=[self.user_1.user_id])
self.field_def_8_name = 'dogandcat'
self.field_def_8 = self._CreateFieldDef(
self.project_1.project_id,
self.field_def_8_name,
'USER_TYPE',
needs_member=True,
needs_perm='EDIT_PROJECT',
notify_on=tracker_pb2.NotifyTriggers.ANY_COMMENT)
self.field_def_9_name = 'catanddog'
self.field_def_9 = self._CreateFieldDef(
self.project_1.project_id,
self.field_def_9_name,
'DATE_TYPE',
date_action_str='ping_owner_only')
self.field_def_10_name = 'url'
self.field_def_10 = self._CreateFieldDef(
self.project_1.project_id, self.field_def_10_name, 'URL_TYPE')
self.field_def_project2_name = 'lorem'
self.field_def_project2 = self._CreateFieldDef(
self.project_2.project_id, self.field_def_project2_name, 'ENUM_TYPE')
self.approval_def_1_name = 'approval_field_1'
self.approval_def_1_id = self._CreateFieldDef(
self.project_1.project_id,
self.approval_def_1_name,
'APPROVAL_TYPE',
docstring='ad_1_docstring',
admin_ids=[self.user_1.user_id])
self.approval_def_1 = tracker_pb2.ApprovalDef(
approval_id=self.approval_def_1_id,
approver_ids=[self.user_2.user_id],
survey='approval_def_1 survey')
self.approval_def_2_name = 'approval_field_1'
self.approval_def_2_id = self._CreateFieldDef(
self.project_1.project_id,
self.approval_def_2_name,
'APPROVAL_TYPE',
docstring='ad_2_docstring',
admin_ids=[self.user_1.user_id])
self.approval_def_2 = tracker_pb2.ApprovalDef(
approval_id=self.approval_def_2_id,
approver_ids=[self.user_2.user_id],
survey='approval_def_2 survey')
approval_defs = [self.approval_def_1, self.approval_def_2]
self.field_def_6_name = 'simonsays'
self.field_def_6 = self._CreateFieldDef(
self.project_1.project_id,
self.field_def_6_name,
'STR_TYPE',
approval_id=self.approval_def_1_id)
self.dne_field_def_id = 999999
self.fv_1_value = u'some_string_field_value'
self.fv_1 = fake.MakeFieldValue(
field_id=self.field_def_1, str_value=self.fv_1_value, derived=False)
self.fv_1_derived = fake.MakeFieldValue(
field_id=self.field_def_1, str_value=self.fv_1_value, derived=True)
self.fv_6 = fake.MakeFieldValue(
field_id=self.field_def_6, str_value=u'touch-nose', derived=False)
self.phase_1_id = 123123
self.phase_1 = fake.MakePhase(self.phase_1_id, name='some phase name')
self.av_1 = fake.MakeApprovalValue(
self.approval_def_1_id,
setter_id=self.user_1.user_id,
set_on=self.PAST_TIME,
approver_ids=[self.user_2.user_id],
phase_id=self.phase_1_id)
self.av_2 = fake.MakeApprovalValue(
self.approval_def_1_id,
setter_id=self.user_1.user_id,
set_on=self.PAST_TIME,
approver_ids=[self.user_2.user_id])
self.issue_1 = fake.MakeTestIssue(
self.project_1.project_id,
1,
'sum',
'New',
self.user_1.user_id,
cc_ids=[self.user_2.user_id],
derived_cc_ids=[self.user_3.user_id],
project_name=self.project_1.project_name,
star_count=1,
labels=['label-a', 'label-b', 'days-1'],
derived_owner_id=self.user_2.user_id,
derived_status='Fixed',
derived_labels=['label-derived', 'OS-mac', 'label-derived-2'],
component_ids=[1, 2],
merged_into_external='b/1',
derived_component_ids=[3, 4],
attachment_count=5,
field_values=[self.fv_1, self.fv_1_derived],
opened_timestamp=self.PAST_TIME,
modified_timestamp=self.PAST_TIME,
approval_values=[self.av_1],
phases=[self.phase_1])
self.issue_2 = fake.MakeTestIssue(
self.project_2.project_id,
2,
'sum2',
None,
None,
reporter_id=self.user_1.user_id,
project_name=self.project_2.project_name,
merged_into=self.issue_1.issue_id,
opened_timestamp=self.PAST_TIME,
modified_timestamp=self.PAST_TIME,
closed_timestamp=self.PAST_TIME,
derived_status='Fixed',
derived_owner_id=self.user_2.user_id,
is_spam=True)
self.services.issue.TestAddIssue(self.issue_1)
self.services.issue.TestAddIssue(self.issue_2)
self.template_0 = self.services.template.TestAddIssueTemplateDef(
11110, self.project_1.project_id, 'template0')
self.template_1_label1_value = '2'
self.template_1_labels = [
'pri-1', '{}-{}'.format(
self.field_def_3_name, self.template_1_label1_value)
]
self.template_1 = self.services.template.TestAddIssueTemplateDef(
11111,
self.project_1.project_id,
'template1',
content='foobar',
summary='foo',
admin_ids=[self.user_2.user_id],
owner_id=self.user_1.user_id,
labels=self.template_1_labels,
component_ids=[654],
field_values=[self.fv_1],
approval_values=[self.av_1],
phases=[self.phase_1])
self.template_2 = self.services.template.TestAddIssueTemplateDef(
11112,
self.project_1.project_id,
'template2',
members_only=True,
owner_defaults_to_member=True)
self.template_3 = self.services.template.TestAddIssueTemplateDef(
11113,
self.project_1.project_id,
'template3',
field_values=[self.fv_1],
approval_values=[self.av_2],
)
self.dne_template = tracker_pb2.TemplateDef(
name='dne_template_name', template_id=11114)
self.labeldef_1 = tracker_pb2.LabelDef(
label='white-mountain',
label_docstring='test label doc string for white-mountain')
self.labeldef_2 = tracker_pb2.LabelDef(
label='yellow-submarine',
label_docstring='Submarine choice for yellow enum field')
self.labeldef_3 = tracker_pb2.LabelDef(
label='yellow-basket',
label_docstring='Basket choice for yellow enum field')
self.labeldef_4 = tracker_pb2.LabelDef(
label='yellow-tasket',
label_docstring='Deprecated tasket choice for yellow enum field',
deprecated=True)
self.labeldef_5 = tracker_pb2.LabelDef(
label='mont-blanc',
label_docstring='test label doc string for mont-blanc',
deprecated=True)
self.predefined_labels = [
self.labeldef_1, self.labeldef_2, self.labeldef_3, self.labeldef_4,
self.labeldef_5
]
test_label_ids = {}
for index, ld in enumerate(self.predefined_labels):
test_label_ids[ld.label] = index
self.services.config.TestAddLabelsDict(test_label_ids)
self.status_1 = tracker_pb2.StatusDef(
status='New', means_open=True, status_docstring='status_1 docstring')
self.status_2 = tracker_pb2.StatusDef(
status='Duplicate',
means_open=False,
status_docstring='status_2 docstring')
self.status_3 = tracker_pb2.StatusDef(
status='Accepted',
means_open=True,
status_docstring='status_3_docstring')
self.status_4 = tracker_pb2.StatusDef(
status='Gibberish',
means_open=True,
status_docstring='status_4_docstring',
deprecated=True)
self.predefined_statuses = [
self.status_1, self.status_2, self.status_3, self.status_4
]
self.component_def_1_path = 'foo'
self.component_def_1_id = self.services.config.CreateComponentDef(
self.cnxn, self.project_1.project_id, self.component_def_1_path,
'cd1_docstring', False, [self.user_1.user_id], [self.user_2.user_id],
self.PAST_TIME, self.user_1.user_id, [0, 1, 2, 3, 4])
self.component_def_2_path = 'foo>bar'
self.component_def_2_id = self.services.config.CreateComponentDef(
self.cnxn, self.project_1.project_id, self.component_def_2_path,
'cd2_docstring', True, [self.user_1.user_id], [self.user_2.user_id],
self.PAST_TIME, self.user_1.user_id, [])
self.services.config.UpdateConfig(
self.cnxn,
self.project_1,
statuses_offer_merge=[self.status_2.status],
excl_label_prefixes=['type', 'priority'],
default_template_for_developers=self.template_2.template_id,
default_template_for_users=self.template_1.template_id,
list_prefs=('ID Summary', 'ID', 'status', 'owner', 'owner:me'),
# UpdateConfig accepts tuples rather than protorpc *Defs
well_known_labels=[
(ld.label, ld.label_docstring, ld.deprecated)
for ld in self.predefined_labels
],
approval_defs=[
(ad.approval_id, ad.approver_ids, ad.survey) for ad in approval_defs
],
well_known_statuses=[
(sd.status, sd.status_docstring, sd.means_open, sd.deprecated)
for sd in self.predefined_statuses
])
# base_query_id 2 equates to "is:open", defined in tracker_constants.
self.psq_1 = tracker_pb2.SavedQuery(
query_id=2, name='psq1 name', base_query_id=2, query='foo=bar')
self.psq_2 = tracker_pb2.SavedQuery(
query_id=3, name='psq2 name', query='fizz=buzz')
self.services.features.UpdateCannedQueries(
self.cnxn, self.project_1.project_id, [self.psq_1, self.psq_2])
def _CreateFieldDef(
self,
project_id,
field_name,
field_type_str,
docstring=None,
min_value=None,
max_value=None,
regex=None,
needs_member=None,
needs_perm=None,
grants_perm=None,
notify_on=None,
date_action_str=None,
admin_ids=None,
editor_ids=None,
is_required=False,
is_niche=False,
is_multivalued=False,
is_phase_field=False,
approval_id=None,
is_restricted_field=False):
"""Calls CreateFieldDef with reasonable defaults, returns the ID."""
if admin_ids is None:
admin_ids = []
if editor_ids is None:
editor_ids = []
return self.services.config.CreateFieldDef(
self.cnxn,
project_id,
field_name,
field_type_str,
None,
None,
is_required,
is_niche,
is_multivalued,
min_value,
max_value,
regex,
needs_member,
needs_perm,
grants_perm,
notify_on,
date_action_str,
docstring,
admin_ids,
editor_ids,
is_phase_field=is_phase_field,
approval_id=approval_id,
is_restricted_field=is_restricted_field)
def _GetFieldDefById(self, project_id, fd_id):
config = self.services.config.GetProjectConfig(self.cnxn, project_id)
return [fd for fd in config.field_defs if fd.field_id == fd_id][0]
def _GetApprovalDefById(self, project_id, ad_id):
config = self.services.config.GetProjectConfig(self.cnxn, project_id)
return [ad for ad in config.approval_defs if ad.approval_id == ad_id][0]
def testConvertHotlist(self):
"""We can convert a Hotlist."""
hotlist = fake.Hotlist(
'Hotlist-Name',
240,
default_col_spec='chicken goose',
is_private=False,
owner_ids=[111],
editor_ids=[222, 333],
summary='Hotlist summary',
description='Hotlist Description')
expected_api_hotlist = feature_objects_pb2.Hotlist(
name='hotlists/240',
display_name=hotlist.name,
owner= 'users/111',
summary=hotlist.summary,
description=hotlist.description,
editors=['users/222', 'users/333'],
hotlist_privacy=feature_objects_pb2.Hotlist.HotlistPrivacy.Value(
'PUBLIC'),
default_columns=[
issue_objects_pb2.IssuesListColumn(column='chicken'),
issue_objects_pb2.IssuesListColumn(column='goose')
])
self.converter.user_auth = authdata.AuthData.FromUser(
self.cnxn, self.user_1, self.services)
self.assertEqual(
expected_api_hotlist, self.converter.ConvertHotlist(hotlist))
def testConvertHotlist_DefaultValues(self):
"""We can convert a Hotlist with some empty or default values."""
hotlist = fake.Hotlist(
'Hotlist-Name',
241,
is_private=True,
owner_ids=[111],
summary='Hotlist summary',
description='Hotlist Description',
default_col_spec='')
expected_api_hotlist = feature_objects_pb2.Hotlist(
name='hotlists/241',
display_name=hotlist.name,
owner='users/111',
summary=hotlist.summary,
description=hotlist.description,
hotlist_privacy=feature_objects_pb2.Hotlist.HotlistPrivacy.Value(
'PRIVATE'))
self.converter.user_auth = authdata.AuthData.FromUser(
self.cnxn, self.user_1, self.services)
self.assertEqual(
expected_api_hotlist, self.converter.ConvertHotlist(hotlist))
def testConvertHotlists(self):
"""We can convert several Hotlists."""
hotlists = [
fake.Hotlist(
'Hotlist-Name',
241,
owner_ids=[111],
summary='Hotlist summary',
description='Hotlist Description'),
fake.Hotlist(
'Hotlist-Name',
241,
owner_ids=[111],
summary='Hotlist summary',
description='Hotlist Description')
]
self.assertEqual(2, len(self.converter.ConvertHotlists(hotlists)))
def testConvertHotlistItems(self):
"""We can convert HotlistItems."""
hotlist_item_fields = [
(self.issue_1.issue_id, 21, 111, self.PAST_TIME, 'note2'),
(78900, 11, 222, self.PAST_TIME, 'note3'), # Does not exist.
(self.issue_2.issue_id, 1, 222, None, 'note1'),
]
hotlist = fake.Hotlist(
'Hotlist-Name', 241, hotlist_item_fields=hotlist_item_fields)
self.converter.user_auth = authdata.AuthData.FromUser(
self.cnxn, self.user_1, self.services)
api_items = self.converter.ConvertHotlistItems(
hotlist.hotlist_id, hotlist.items)
expected_create_time = timestamp_pb2.Timestamp()
expected_create_time.FromSeconds(self.PAST_TIME)
expected_items = [
feature_objects_pb2.HotlistItem(
name='hotlists/241/items/proj.1',
issue='projects/proj/issues/1',
rank=1,
adder= 'users/111',
create_time=expected_create_time,
note='note2'),
feature_objects_pb2.HotlistItem(
name='hotlists/241/items/goose.2',
issue='projects/goose/issues/2',
rank=0,
adder='users/222',
note='note1')
]
self.assertEqual(api_items, expected_items)
def testConvertHotlistItems_Empty(self):
hotlist = fake.Hotlist('Hotlist-Name', 241)
self.converter.user_auth = authdata.AuthData.FromUser(
self.cnxn, self.user_1, self.services)
api_items = self.converter.ConvertHotlistItems(
hotlist.hotlist_id, hotlist.items)
self.assertEqual(api_items, [])
@mock.patch('tracker.attachment_helpers.SignAttachmentID')
def testConvertComments(self, mock_SignAttachmentID):
"""We can convert comments."""
mock_SignAttachmentID.return_value = 2
attach = tracker_pb2.Attachment(
attachment_id=1,
mimetype='image/png',
filename='example.png',
filesize=12345)
deleted_attach = tracker_pb2.Attachment(
attachment_id=2,
mimetype='image/png',
filename='deleted_example.png',
filesize=67890,
deleted=True)
initial_comment = tracker_pb2.IssueComment(
project_id=self.issue_1.project_id,
issue_id=self.issue_1.issue_id,
user_id=self.issue_1.reporter_id,
timestamp=self.PAST_TIME,
content='initial description',
sequence=0,
is_description=True,
description_num='1',
attachments=[attach, deleted_attach])
deleted_comment = tracker_pb2.IssueComment(
project_id=self.issue_1.project_id,
issue_id=self.issue_1.issue_id,
timestamp=self.PAST_TIME,
deleted_by=self.issue_1.reporter_id,
sequence=1)
amendments = [
tracker_pb2.Amendment(
field=tracker_pb2.FieldID.SUMMARY, newvalue='new', oldvalue='old'),
tracker_pb2.Amendment(
field=tracker_pb2.FieldID.OWNER, added_user_ids=[111]),
tracker_pb2.Amendment(
field=tracker_pb2.FieldID.CC,
added_user_ids=[111],
removed_user_ids=[222]),
tracker_pb2.Amendment(
field=tracker_pb2.FieldID.CUSTOM,
custom_field_name='EstDays',
newvalue='12')
]
amendments_comment = tracker_pb2.IssueComment(
project_id=self.issue_1.project_id,
issue_id=self.issue_1.issue_id,
user_id=self.issue_1.reporter_id,
timestamp=self.PAST_TIME,
content='some amendments',
sequence=2,
amendments=amendments,
importer_id=1, # Not used in conversion, so nothing to verify.
approval_id=self.approval_def_1_id)
inbound_spam_comment = tracker_pb2.IssueComment(
project_id=self.issue_1.project_id,
issue_id=self.issue_1.issue_id,
user_id=self.issue_1.reporter_id,
timestamp=self.PAST_TIME,
content='content',
sequence=3,
inbound_message='inbound message',
is_spam=True)
expected_0 = issue_objects_pb2.Comment(
name='projects/proj/issues/1/comments/0',
state=issue_objects_pb2.IssueContentState.Value('ACTIVE'),
type=issue_objects_pb2.Comment.Type.Value('DESCRIPTION'),
content='initial description',
commenter='users/111',
create_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
attachments=[
issue_objects_pb2.Comment.Attachment(
filename='example.png',
state=issue_objects_pb2.IssueContentState.Value('ACTIVE'),
size=12345,
media_type='image/png',
thumbnail_uri='attachment?aid=1&signed_aid=2&inline=1&thumb=1',
view_uri='attachment?aid=1&signed_aid=2&inline=1',
download_uri='attachment?aid=1&signed_aid=2'),
issue_objects_pb2.Comment.Attachment(
filename='deleted_example.png',
state=issue_objects_pb2.IssueContentState.Value('DELETED'),
media_type='image/png')
])
expected_1 = issue_objects_pb2.Comment(
name='projects/proj/issues/1/comments/1',
state=issue_objects_pb2.IssueContentState.Value('DELETED'),
type=issue_objects_pb2.Comment.Type.Value('COMMENT'),
create_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME))
expected_2 = issue_objects_pb2.Comment(
name='projects/proj/issues/1/comments/2',
state=issue_objects_pb2.IssueContentState.Value('ACTIVE'),
type=issue_objects_pb2.Comment.Type.Value('COMMENT'),
content='some amendments',
commenter='users/111',
create_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
approval='projects/proj/approvalDefs/%d' % self.approval_def_1_id,
amendments=[
issue_objects_pb2.Comment.Amendment(
field_name='Summary', new_or_delta_value='new',
old_value='old'),
issue_objects_pb2.Comment.Amendment(
field_name='Owner', new_or_delta_value='o...@example.com'),
issue_objects_pb2.Comment.Amendment(
field_name='Cc',
new_or_delta_value='-t...@example.com o...@example.com'),
issue_objects_pb2.Comment.Amendment(
field_name='EstDays', new_or_delta_value='12')
])
expected_3 = issue_objects_pb2.Comment(
name='projects/proj/issues/1/comments/3',
state=issue_objects_pb2.IssueContentState.Value('SPAM'),
type=issue_objects_pb2.Comment.Type.Value('COMMENT'),
content='content',
commenter='users/111',
create_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
inbound_message='inbound message')
comments = [
initial_comment, deleted_comment, amendments_comment,
inbound_spam_comment
]
actual = self.converter.ConvertComments(self.issue_1.issue_id, comments)
self.assertEqual(actual, [expected_0, expected_1, expected_2, expected_3])
def testConvertComments_Empty(self):
"""We can convert an empty list of comments."""
self.assertEqual(
self.converter.ConvertComments(self.issue_1.issue_id, []), [])
def testConvertIssue(self):
"""We can convert a single issue."""
self.assertEqual(self.converter.ConvertIssue(self.issue_1),
self.converter.ConvertIssues([self.issue_1])[0])
def testConvertIssues(self):
"""We can convert Issues."""
blocked_on_1 = fake.MakeTestIssue(
self.project_1.project_id,
3,
'sum3',
'New',
self.user_1.user_id,
issue_id=301,
project_name=self.project_1.project_name,
)
blocked_on_2 = fake.MakeTestIssue(
self.project_2.project_id,
4,
'sum4',
'New',
self.user_1.user_id,
issue_id=401,
project_name=self.project_2.project_name,
)
blocking = fake.MakeTestIssue(
self.project_2.project_id,
5,
'sum5',
'New',
self.user_1.user_id,
issue_id=501,
project_name=self.project_2.project_name,
)
self.services.issue.TestAddIssue(blocked_on_1)
self.services.issue.TestAddIssue(blocked_on_2)
self.services.issue.TestAddIssue(blocking)
# Reversing natural ordering to ensure order is respected.
self.issue_1.blocked_on_iids = [
blocked_on_2.issue_id, blocked_on_1.issue_id
]
self.issue_1.dangling_blocked_on_refs = [
tracker_pb2.DanglingIssueRef(ext_issue_identifier='b/555'),
tracker_pb2.DanglingIssueRef(ext_issue_identifier='b/2')
]
self.issue_1.blocking_iids = [blocking.issue_id]
self.issue_1.dangling_blocking_refs = [
tracker_pb2.DanglingIssueRef(ext_issue_identifier='b/3')
]
issues = [self.issue_1, self.issue_2]
expected_1 = issue_objects_pb2.Issue(
name='projects/proj/issues/1',
summary='sum',
state=issue_objects_pb2.IssueContentState.Value('ACTIVE'),
status=issue_objects_pb2.Issue.StatusValue(
derivation=EXPLICIT_DERIVATION, status='New'),
reporter='users/111',
owner=issue_objects_pb2.Issue.UserValue(
derivation=EXPLICIT_DERIVATION, user='users/111'),
cc_users=[
issue_objects_pb2.Issue.UserValue(
derivation=EXPLICIT_DERIVATION, user='users/222'),
issue_objects_pb2.Issue.UserValue(
derivation=RULE_DERIVATION, user='users/333')
],
labels=[
issue_objects_pb2.Issue.LabelValue(
derivation=EXPLICIT_DERIVATION, label='label-a'),
issue_objects_pb2.Issue.LabelValue(
derivation=EXPLICIT_DERIVATION, label='label-b'),
issue_objects_pb2.Issue.LabelValue(
derivation=RULE_DERIVATION, label='label-derived'),
issue_objects_pb2.Issue.LabelValue(
derivation=RULE_DERIVATION, label='label-derived-2')
],
components=[
issue_objects_pb2.Issue.ComponentValue(
derivation=EXPLICIT_DERIVATION,
component='projects/proj/componentDefs/1'),
issue_objects_pb2.Issue.ComponentValue(
derivation=EXPLICIT_DERIVATION,
component='projects/proj/componentDefs/2'),
issue_objects_pb2.Issue.ComponentValue(
derivation=RULE_DERIVATION,
component='projects/proj/componentDefs/3'),
issue_objects_pb2.Issue.ComponentValue(
derivation=RULE_DERIVATION,
component='projects/proj/componentDefs/4'),
],
field_values=[
issue_objects_pb2.FieldValue(
derivation=EXPLICIT_DERIVATION,
field='projects/proj/fieldDefs/%d' % self.field_def_1,
value=self.fv_1_value,
),
issue_objects_pb2.FieldValue(
derivation=RULE_DERIVATION,
field='projects/proj/fieldDefs/%d' % self.field_def_1,
value=self.fv_1_value,
),
issue_objects_pb2.FieldValue(
derivation=EXPLICIT_DERIVATION,
field='projects/proj/fieldDefs/%d' % self.field_def_3,
value='1',
),
issue_objects_pb2.FieldValue(
derivation=RULE_DERIVATION,
field='projects/proj/fieldDefs/%d' % self.field_def_4,
value='mac',
)
],
merged_into_issue_ref=issue_objects_pb2.IssueRef(ext_identifier='b/1'),
blocked_on_issue_refs=[
issue_objects_pb2.IssueRef(issue='projects/goose/issues/4'),
issue_objects_pb2.IssueRef(issue='projects/proj/issues/3'),
issue_objects_pb2.IssueRef(ext_identifier='b/555'),
issue_objects_pb2.IssueRef(ext_identifier='b/2')
],
blocking_issue_refs=[
issue_objects_pb2.IssueRef(issue='projects/goose/issues/5'),
issue_objects_pb2.IssueRef(ext_identifier='b/3')
],
create_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
component_modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
status_modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
owner_modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
star_count=1,
attachment_count=5,
phases=[self.phase_1.name])
expected_2 = issue_objects_pb2.Issue(
name='projects/goose/issues/2',
summary='sum2',
state=issue_objects_pb2.IssueContentState.Value('SPAM'),
status=issue_objects_pb2.Issue.StatusValue(
derivation=RULE_DERIVATION, status='Fixed'),
reporter='users/111',
owner=issue_objects_pb2.Issue.UserValue(
derivation=RULE_DERIVATION, user='users/222'),
merged_into_issue_ref=issue_objects_pb2.IssueRef(
issue='projects/proj/issues/1'),
create_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
close_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
component_modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
status_modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
owner_modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME))
self.assertEqual(
self.converter.ConvertIssues(issues), [expected_1, expected_2])
def testConvertIssues_Empty(self):
"""ConvertIssues works with no issues passed in."""
self.assertEqual(self.converter.ConvertIssues([]), [])
def testConvertIssues_NegativeAttachmentCount(self):
"""Negative attachment counts are not set on issues."""
issue = fake.MakeTestIssue(
self.project_1.project_id,
3,
'sum',
'New',
owner_id=None,
reporter_id=111,
attachment_count=-10,
project_name=self.project_1.project_name,
opened_timestamp=self.PAST_TIME,
modified_timestamp=self.PAST_TIME)
self.services.issue.TestAddIssue(issue)
expected_issue = issue_objects_pb2.Issue(
name='projects/proj/issues/3',
state=issue_objects_pb2.IssueContentState.Value('ACTIVE'),
summary='sum',
status=issue_objects_pb2.Issue.StatusValue(
derivation=EXPLICIT_DERIVATION, status='New'),
reporter='users/111',
create_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
component_modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
status_modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
owner_modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
)
self.assertEqual(self.converter.ConvertIssues([issue]), [expected_issue])
def testConvertIssues_FilterApprovalFV(self):
issue = fake.MakeTestIssue(
self.project_1.project_id,
3,
'sum',
'New',
owner_id=None,
reporter_id=111,
attachment_count=-10,
project_name=self.project_1.project_name,
opened_timestamp=self.PAST_TIME,
modified_timestamp=self.PAST_TIME,
field_values=[self.fv_1, self.fv_6])
self.services.issue.TestAddIssue(issue)
actual = self.converter.ConvertIssues([issue])[0]
expected_fv = issue_objects_pb2.FieldValue(
derivation=EXPLICIT_DERIVATION,
field='projects/proj/fieldDefs/%d' % self.field_def_1,
value=self.fv_1_value,
)
self.assertEqual(len(actual.field_values), 1)
self.assertEqual(actual.field_values[0], expected_fv)
def testConvertUser(self):
"""We can convert a single User."""
self.user_1.vacation_message = 'non-empty-string'
self.converter.user_auth = authdata.AuthData.FromUser(
self.cnxn, self.user_1, self.services)
expected_user = user_objects_pb2.User(
name='users/111',
display_name='one@example.com',
email='one@example.com',
availability_message='non-empty-string')
self.assertEqual(self.converter.ConvertUser(self.user_1), expected_user)
def testConvertUsers(self):
user_deleted = self.services.user.TestAddUser(
'', framework_constants.DELETED_USER_ID)
self.user_1.vacation_message = 'non-empty-string'
user_ids = [self.user_1.user_id, user_deleted.user_id]
self.converter.user_auth = authdata.AuthData.FromUser(
self.cnxn, self.user_1, self.services)
expected_user_dict = {
self.user_1.user_id:
user_objects_pb2.User(
name='users/111',
display_name='one@example.com',
email='one@example.com',
availability_message='non-empty-string'),
user_deleted.user_id:
user_objects_pb2.User(
name='users/1',
display_name=framework_constants.DELETED_USER_NAME,
email='',
availability_message='User never visited'),
}
self.assertEqual(self.converter.ConvertUsers(user_ids), expected_user_dict)
def testConvertProjectStars(self):
expected_stars = [
user_objects_pb2.ProjectStar(name='users/111/projectStars/proj'),
user_objects_pb2.ProjectStar(name='users/111/projectStars/goose')
]
self.assertEqual(
self.converter.ConvertProjectStars(
self.user_1.user_id, [self.project_1, self.project_2]),
expected_stars)
def _Issue(self, project_id, local_id):
issue = tracker_pb2.Issue(owner_id=0)
issue.project_name = 'proj-%d' % project_id
issue.project_id = project_id
issue.local_id = local_id
issue.issue_id = project_id * 100 + local_id
return issue
def testIngestAttachmentUploads(self):
up_1 = issues_pb2.AttachmentUpload(
filename='clown.gif', content='iTs prOUnOuNcED JIF')
up_2 = issues_pb2.AttachmentUpload(
filename='mowgli', content='cutest dog')
ingested = self.converter.IngestAttachmentUploads([up_1, up_2])
expected = [framework_helpers.AttachmentUpload(
'clown.gif', 'iTs prOUnOuNcED JIF', 'image/gif'),
framework_helpers.AttachmentUpload(
'mowgli', 'cutest dog', 'text/plain')]
self.assertEqual(ingested, expected)
def testtIngestAttachmentUploads_Invalid(self):
up_1 = issues_pb2.AttachmentUpload(filename='clown.gif')
up_2 = issues_pb2.AttachmentUpload(content='cutest dog')
with self.assertRaisesRegexp(
exceptions.InputException, 'Uploaded .+\nUploaded .+'):
self.converter.IngestAttachmentUploads([up_1, up_2])
def testIngestIssueDeltas(self):
# Set up.
self.services.project.TestAddProject('proj-780', project_id=780)
config = fake.MakeTestConfig(780, [], [])
self.services.config.StoreConfig(self.cnxn, config)
issue_1 = self._Issue(780, 1)
self.services.issue.TestAddIssue(issue_1)
issue_2 = self._Issue(780, 2)
self.services.issue.TestAddIssue(issue_2)
comp_1 = fake.MakeTestComponentDef(780, 1)
comp_2 = fake.MakeTestComponentDef(780, 2)
fd_str = fake.MakeTestFieldDef(1, 780, tracker_pb2.FieldTypes.STR_TYPE)
fd_enum = fake.MakeTestFieldDef(
2, 780, tracker_pb2.FieldTypes.ENUM_TYPE, field_name='Kingdom')
config = fake.MakeTestConfig(780, [], [])
config.component_defs = [comp_1, comp_2]
config.field_defs = [fd_str, fd_enum]
self.services.config.StoreConfig(self.cnxn, config)
# Issue and delta that changes all things.
api_issue_all = issue_objects_pb2.Issue(
name='projects/proj-780/issues/1',
status=issue_objects_pb2.Issue.StatusValue(status='Fixed'),
owner=issue_objects_pb2.Issue.UserValue(user='users/111'),
summary='honk honk.',
cc_users=[issue_objects_pb2.Issue.UserValue(user='users/222')],
components=[
issue_objects_pb2.Issue.ComponentValue(
component='projects/proj-780/componentDefs/1')
],
field_values=[
issue_objects_pb2.FieldValue(
field='projects/proj-780/fieldDefs/1', value='chicken'),
issue_objects_pb2.FieldValue(
field='projects/proj-780/fieldDefs/2', value='come')
],
labels=[issue_objects_pb2.Issue.LabelValue(label='ready')])
mask_all = field_mask_pb2.FieldMask(
paths=[
'status', 'owner', 'summary', 'cc_users', 'labels', 'components',
'field_values'
])
api_delta_all = issues_pb2.IssueDelta(
issue=api_issue_all,
update_mask=mask_all,
ccs_remove=['users/333'],
components_remove=['projects/proj-780/componentDefs/2'],
field_vals_remove=[
issue_objects_pb2.FieldValue(
field='projects/proj-780/fieldDefs/1', value='rooster'),
issue_objects_pb2.FieldValue(
field='projects/proj-780/fieldDefs/2', value='leave')
],
labels_remove=['not-ready'])
exp_fvs_add = [
field_helpers.ParseOneFieldValue(
self.cnxn, self.services.user, fd_str, 'chicken')
]
exp_fvs_remove = [
field_helpers.ParseOneFieldValue(
self.cnxn, self.services.user, fd_str, 'rooster')
]
expected_delta_all = tracker_pb2.IssueDelta(
status='Fixed',
owner_id=111,
summary='honk honk.',
cc_ids_add=[222],
cc_ids_remove=[333],
comp_ids_add=[1],
comp_ids_remove=[2],
field_vals_add=exp_fvs_add,
field_vals_remove=exp_fvs_remove,
labels_add=['ready', 'Kingdom-come'],
labels_remove=['not-ready', 'Kingdom-leave'])
api_deltas = [api_delta_all]
# Issue with all fields, but an empty mask.
api_issue_all_masked = issue_objects_pb2.Issue(
name='projects/proj-780/issues/2',
status=issue_objects_pb2.Issue.StatusValue(status='Fixed'),
owner=issue_objects_pb2.Issue.UserValue(user='users/111'),
summary='honk honk.',
cc_users=[issue_objects_pb2.Issue.UserValue(user='users/222')],
components=[
issue_objects_pb2.Issue.ComponentValue(
component='projects/proj-780/componentDefs/1')
],
field_values=[
issue_objects_pb2.FieldValue(
field='projects/proj-780/fieldDefs/1', value='chicken'),
issue_objects_pb2.FieldValue(
field='projects/proj-780/fieldDefs/2', value='come')
],
labels=[issue_objects_pb2.Issue.LabelValue(label='ready')])
api_delta_all_masked = issues_pb2.IssueDelta(
issue=api_issue_all_masked,
update_mask=field_mask_pb2.FieldMask(paths=[]),
ccs_remove=['users/333'],
components_remove=['projects/proj-780/componentDefs/2'],
field_vals_remove=[
issue_objects_pb2.FieldValue(
field='projects/proj-780/fieldDefs/1', value='rooster'),
issue_objects_pb2.FieldValue(
field='projects/proj-780/fieldDefs/2', value='leave')
],
labels_remove=['not-ready'])
expected_delta_all_masked = tracker_pb2.IssueDelta(
cc_ids_remove=[333],
comp_ids_remove=[2],
labels_remove=['not-ready', 'Kingdom-leave'],
field_vals_remove=exp_fvs_remove)
api_deltas.append(api_delta_all_masked)
actual = self.converter.IngestIssueDeltas(api_deltas)
expected = [(78001, expected_delta_all), (78002, expected_delta_all_masked)]
self.assertEqual(actual, expected)
def testIngestIssueDeltas_IssueRefs(self):
# Set up.
self.services.project.TestAddProject('proj-780', project_id=780)
issue = self._Issue(780, 1)
self.services.issue.TestAddIssue(issue)
bo_add = self._Issue(780, 2)
self.services.issue.TestAddIssue(bo_add)
b_add = self._Issue(780, 3)
self.services.issue.TestAddIssue(b_add)
bo_remove = self._Issue(780, 4)
self.services.issue.TestAddIssue(bo_remove)
b_remove = self._Issue(780, 5)
self.services.issue.TestAddIssue(b_remove)
# merge_remove tested in testIngestIssueDeltas_RemoveNonRepeated
merge_add = self._Issue(780, 6)
self.services.issue.TestAddIssue(merge_add)
api_issue = issue_objects_pb2.Issue(
name='projects/proj-780/issues/1',
blocked_on_issue_refs=[
issue_objects_pb2.IssueRef(issue='projects/proj-780/issues/2'),
issue_objects_pb2.IssueRef(ext_identifier='b/1')
],
blocking_issue_refs=[
issue_objects_pb2.IssueRef(issue='projects/proj-780/issues/3'),
issue_objects_pb2.IssueRef(ext_identifier='b/2')
],
merged_into_issue_ref=issue_objects_pb2.IssueRef(
issue='projects/proj-780/issues/6'))
api_delta = issues_pb2.IssueDelta(
issue=api_issue,
update_mask=field_mask_pb2.FieldMask(
paths=[
'blocked_on_issue_refs', 'blocking_issue_refs',
'merged_into_issue_ref'
]),
blocked_on_issues_remove=[
issue_objects_pb2.IssueRef(issue='projects/proj-780/issues/4'),
issue_objects_pb2.IssueRef(ext_identifier='b/3')
],
blocking_issues_remove=[
issue_objects_pb2.IssueRef(issue='projects/proj-780/issues/5'),
issue_objects_pb2.IssueRef(ext_identifier='b/4')
])
expected_delta = tracker_pb2.IssueDelta(
blocked_on_add=[bo_add.issue_id],
blocked_on_remove=[bo_remove.issue_id],
blocking_add=[b_add.issue_id],
blocking_remove=[b_remove.issue_id],
ext_blocked_on_add=['b/1'],
ext_blocked_on_remove=['b/3'],
ext_blocking_add=['b/2'],
ext_blocking_remove=['b/4'],
merged_into=merge_add.issue_id)
# Test adding an external merged_into_issue.
api_issue_ext_merged = issue_objects_pb2.Issue(
name='projects/proj-780/issues/2',
merged_into_issue_ref=issue_objects_pb2.IssueRef(ext_identifier='b/1'))
api_delta_ext_merged = issues_pb2.IssueDelta(
issue=api_issue_ext_merged,
update_mask=field_mask_pb2.FieldMask(paths=['merged_into_issue_ref']))
expected_delta_ext_merged = tracker_pb2.IssueDelta(
merged_into_external='b/1')
# Test issue with empty mask.
issue_all_masked = self._Issue(780, 11)
self.services.issue.TestAddIssue(issue_all_masked)
api_issue_all_masked = copy.deepcopy(api_issue)
api_issue_all_masked.name = 'projects/proj-780/issues/11'
api_delta_all_masked = issues_pb2.IssueDelta(
issue=api_issue_all_masked, update_mask=field_mask_pb2.FieldMask())
expected_all_masked_delta = tracker_pb2.IssueDelta()
# Check results.
actual = self.converter.IngestIssueDeltas(
[api_delta, api_delta_ext_merged, api_delta_all_masked])
expected = [
(78001, expected_delta), (78002, expected_delta_ext_merged),
(78011, expected_all_masked_delta)
]
self.assertEqual(actual, expected)
def testIngestIssueDeltas_OwnerAndOwnerDotUser(self):
# Set up.
self.services.project.TestAddProject('proj-780', project_id=780)
issue = self._Issue(780, 1)
self.services.issue.TestAddIssue(issue)
api_issue = issue_objects_pb2.Issue(
name='projects/proj-780/issues/1',
owner=issue_objects_pb2.Issue.UserValue(user='users/111')
)
# Expect ingest to work when update_mask has just 'owner'.
api_delta = issues_pb2.IssueDelta(
issue=api_issue,
update_mask=field_mask_pb2.FieldMask(paths=['owner'])
)
expected_delta = tracker_pb2.IssueDelta(owner_id=111)
expected = [(78001, expected_delta)]
actual = self.converter.IngestIssueDeltas([api_delta])
self.assertEqual(actual, expected)
# Expect ingest to also work when update_mask uses 'owner.user' instead.
api_delta = issues_pb2.IssueDelta(
issue=api_issue,
update_mask=field_mask_pb2.FieldMask(paths=['owner.user'])
)
actual = self.converter.IngestIssueDeltas([api_delta])
self.assertEqual(actual, expected)
def testIngestIssueDeltas_StatusAndStatusDotStatus(self):
# Set up.
self.services.project.TestAddProject('proj-780', project_id=780)
issue = self._Issue(780, 1)
self.services.issue.TestAddIssue(issue)
api_issue = issue_objects_pb2.Issue(
name='projects/proj-780/issues/1',
owner=issue_objects_pb2.Issue.UserValue(user='users/111'),
status=issue_objects_pb2.Issue.StatusValue(status='New')
)
# Expect ingest to work when update_mask has just 'status'.
api_delta = issues_pb2.IssueDelta(
issue=api_issue,
update_mask=field_mask_pb2.FieldMask(paths=['status'])
)
expected_delta = tracker_pb2.IssueDelta(status='New')
expected = [(78001, expected_delta)]
actual = self.converter.IngestIssueDeltas([api_delta])
self.assertEqual(actual, expected)
# Expect ingest to also work when update_mask uses 'status.status' instead.
api_delta = issues_pb2.IssueDelta(
issue=api_issue,
update_mask=field_mask_pb2.FieldMask(paths=['status.status'])
)
actual = self.converter.IngestIssueDeltas([api_delta])
self.assertEqual(actual, expected)
def testIngestIssueDeltas_RemoveNonRepeated(self):
# Set up.
self.services.project.TestAddProject('proj-780', project_id=780)
issue_1 = self._Issue(780, 1)
self.services.issue.TestAddIssue(issue_1)
issue_2 = self._Issue(780, 2)
self.services.issue.TestAddIssue(issue_2)
# Check we can remove fields without specifying them in the
# issue, as long as they're specified in the FieldMask.
api_issue = issue_objects_pb2.Issue(
name='projects/proj-780/issues/1')
api_delta = issues_pb2.IssueDelta(
issue=api_issue,
update_mask=field_mask_pb2.FieldMask(
paths=[
'owner.user', 'status.status', 'summary',
'merged_into_issue_ref.issue'
]))
# Check thet setting fields to '' result in same behavior as not
# explicitly setting the values at all.
api_issue_set = issue_objects_pb2.Issue(
name='projects/proj-780/issues/2',
summary='',
status=issue_objects_pb2.Issue.StatusValue(status=''),
owner=issue_objects_pb2.Issue.UserValue(user=''),
merged_into_issue_ref=issue_objects_pb2.IssueRef(issue=''))
api_delta_set = issues_pb2.IssueDelta(
issue=api_issue_set,
update_mask=field_mask_pb2.FieldMask(
paths=[
'owner.user', 'status.status', 'summary',
'merged_into_issue_ref.issue'
]))
expected_delta = tracker_pb2.IssueDelta(
owner_id=framework_constants.NO_USER_SPECIFIED,
status='',
summary='',
merged_into=0)
actual = self.converter.IngestIssueDeltas([api_delta, api_delta_set])
expected = [(78001, expected_delta), (78002, expected_delta)]
self.assertEqual(actual, expected)
def testIngestIssueDeltas_InvalidMask(self):
self.services.project.TestAddProject('proj-780', project_id=780)
issue_1 = self._Issue(780, 1)
self.services.issue.TestAddIssue(issue_1)
issue_2 = self._Issue(780, 2)
self.services.issue.TestAddIssue(issue_2)
issue_3 = self._Issue(780, 3)
self.services.issue.TestAddIssue(issue_3)
api_deltas = []
err_msgs = []
api_issue_1 = issue_objects_pb2.Issue(name='projects/proj-780/issues/1')
api_delta_1 = issues_pb2.IssueDelta(issue=api_issue_1)
api_deltas.append(api_delta_1)
err_msgs.append(
'`update_mask` must be set for projects/proj-780/issues/1 delta.')
api_issue_2 = issue_objects_pb2.Issue(name='projects/proj-780/issues/2')
api_delta_2 = issues_pb2.IssueDelta(
issue=api_issue_2,
update_mask=field_mask_pb2.FieldMask()) # Empty but set is fine.
api_deltas.append(api_delta_2)
api_issue_3 = issue_objects_pb2.Issue(name='projects/proj-780/issues/3')
api_delta_3 = issues_pb2.IssueDelta(
issue=api_issue_3,
update_mask=field_mask_pb2.FieldMask(paths=['chicken']))
api_deltas.append(api_delta_3)
err_msgs.append(
'Invalid `update_mask` for projects/proj-780/issues/3 delta.')
with self.assertRaisesRegexp(exceptions.InputException,
'\n'.join(err_msgs)):
self.converter.IngestIssueDeltas(api_deltas)
def testIngestIssueDeltas_OutputOnlyIgnored(self):
# Set up.
self.services.project.TestAddProject('proj-780', project_id=780)
issue_1 = self._Issue(780, 1)
self.services.issue.TestAddIssue(issue_1)
comp_1 = fake.MakeTestComponentDef(780, 1)
fd_str = fake.MakeTestFieldDef(1, 780, tracker_pb2.FieldTypes.STR_TYPE)
config = fake.MakeTestConfig(780, [], [])
config.component_defs = [comp_1]
config.field_defs = [fd_str]
self.services.config.StoreConfig(self.cnxn, config)
api_issue = issue_objects_pb2.Issue(
name='projects/proj-780/issues/1',
owner=issue_objects_pb2.Issue.UserValue(
user='users/111',
derivation=issue_objects_pb2.Derivation.Value('RULE')),
status=issue_objects_pb2.Issue.StatusValue(
status='KingdomCome',
derivation=issue_objects_pb2.Derivation.Value('RULE')),
state=issue_objects_pb2.IssueContentState.Value('DELETED'),
reporter='users/222',
cc_users=[
issue_objects_pb2.Issue.UserValue(
user='users/333',
derivation=issue_objects_pb2.Derivation.Value('RULE'))
],
labels=[
issue_objects_pb2.Issue.LabelValue(
label='wikipedia-sections',
derivation=issue_objects_pb2.Derivation.Value('RULE'))
],
components=[
issue_objects_pb2.Issue.ComponentValue(
component='projects/proj-780/componentDefs/1',
derivation=issue_objects_pb2.Derivation.Value('RULE'))
],
field_values=[
issue_objects_pb2.FieldValue(
field='projects/proj-780/fieldDefs/1',
value='bugs',
derivation=issue_objects_pb2.Derivation.Value('RULE'))
],
create_time=timestamp_pb2.Timestamp(seconds=4044242),
close_time=timestamp_pb2.Timestamp(seconds=4044242),
modify_time=timestamp_pb2.Timestamp(seconds=4044242),
component_modify_time=timestamp_pb2.Timestamp(seconds=4044242),
status_modify_time=timestamp_pb2.Timestamp(seconds=4044242),
owner_modify_time=timestamp_pb2.Timestamp(seconds=4044242),
attachment_count=4,
star_count=2,
phases=['EarlyLife', 'CrimesBegin', 'CrimesContinue'])
paths_with_output_only = [
'owner', 'status', 'state', 'reporter', 'cc_users', 'labels',
'components', 'field_values', 'create_time', 'close_time',
'modify_time', 'component_modify_time', 'status_modify_time',
'owner_modify_time', 'attachment_count', 'star_count', 'phases']
api_delta = issues_pb2.IssueDelta(
issue=api_issue,
update_mask=field_mask_pb2.FieldMask(paths=paths_with_output_only))
expected_delta = tracker_pb2.IssueDelta(
# We ignore all Issue.*Value.derivation OUTPUT_ONLY fields.
owner_id=111,
status='KingdomCome',
cc_ids_add=[333],
labels_add=['wikipedia-sections'],
comp_ids_add=[1],
field_vals_add=[
field_helpers.ParseOneFieldValue(
self.cnxn, self.services.user, fd_str, 'bugs')
])
actual = self.converter.IngestIssueDeltas([api_delta])
expected = [(78001, expected_delta)]
self.assertEqual(actual, expected)
def testIngestIssueDeltas_Empty(self):
actual = self.converter.IngestIssueDeltas([])
self.assertEqual(actual, [])
def testIngestIssueDeltas_InvalidValuesForFields(self):
# Set up.
self.services.project.TestAddProject('proj-780', project_id=780)
issue_1 = self._Issue(780, 1)
self.services.issue.TestAddIssue(issue_1)
fd_int = fake.MakeTestFieldDef(1, 780, tracker_pb2.FieldTypes.INT_TYPE)
fd_date = fake.MakeTestFieldDef(2, 780, tracker_pb2.FieldTypes.DATE_TYPE)
config = fake.MakeTestConfig(780, [], [])
config.field_defs = [fd_int, fd_date]
self.services.config.StoreConfig(self.cnxn, config)
api_issue = issue_objects_pb2.Issue(
name='projects/proj-780/issues/1',
field_values=[
issue_objects_pb2.FieldValue(
field='projects/proj-780/fieldDefs/1',
value='NotAnInt',
derivation=issue_objects_pb2.Derivation.Value('RULE')),
issue_objects_pb2.FieldValue(
field='projects/proj-780/fieldDefs/2',
value='NoDate',
derivation=issue_objects_pb2.Derivation.Value('EXPLICIT')),
],
)
api_delta = issues_pb2.IssueDelta(
issue=api_issue,
update_mask=field_mask_pb2.FieldMask(paths=['field_values']))
error_messages = [
r'Could not ingest value \(NotAnInt\) for FieldDef \(projects/proj-780/'
r'fieldDefs/1\): Could not parse NotAnInt',
r'Could not ingest value \(NoDate\) for FieldDef \(projects/proj-780/fi'
r'eldDefs/2\): Could not parse NoDate',
]
error_messages_re = '\n'.join(error_messages)
with self.assertRaisesRegexp(exceptions.InputException, error_messages_re):
self.converter.IngestIssueDeltas([api_delta])
@mock.patch('time.time', mock.MagicMock(return_value=CURRENT_TIME))
def testIngestApprovalDeltas(self):
mask = field_mask_pb2.FieldMask(
paths=['approvers', 'status', 'setter', 'phase', 'set_time'])
av_name = (
'projects/proj/issues/1/approvalValues/%d' % self.approval_def_1_id)
approval_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(
name=av_name,
status=issue_objects_pb2.ApprovalValue.ApprovalStatus.Value('NA'),
approvers=['users/222', 'users/333'],
approval_def='ignored',
set_time=timestamp_pb2.Timestamp(), # Ignored.
setter='ignored',
phase='ignored'),
update_mask=mask,
approvers_remove=['users/222'])
actual = self.converter.IngestApprovalDeltas(
[approval_delta], self.user_1.user_id)
expected_delta = tracker_pb2.ApprovalDelta(
status=tracker_pb2.ApprovalStatus.NA,
setter_id=self.user_1.user_id,
set_on=int(CURRENT_TIME),
approver_ids_add=[222, 333],
approver_ids_remove=[222],
)
expected_delta_specifications = [
(self.issue_1.issue_id, self.approval_def_1_id, expected_delta)
]
self.assertEqual(actual, expected_delta_specifications)
def testIngestApprovalDeltas_EmptyMask(self):
av_name = (
'projects/proj/issues/1/approvalValues/%d' % self.approval_def_1_id)
# field_def_6 belongs to approval_def_1.
approval_fv = issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_6, value=u'x')
approval_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(
name=av_name,
status=issue_objects_pb2.ApprovalValue.ApprovalStatus.Value('NA'),
approvers=['users/222', 'users/333'],
approval_def='ignored',
field_values=[approval_fv],
set_time=timestamp_pb2.Timestamp(), # Ignored.
setter='ignored',
phase='ignored'),
update_mask=field_mask_pb2.FieldMask(),
approvers_remove=['users/222'])
actual = self.converter.IngestApprovalDeltas(
[approval_delta], self.user_1.user_id)
expected_delta = tracker_pb2.ApprovalDelta(approver_ids_remove=[222])
expected_delta_specifications = [
(self.issue_1.issue_id, self.approval_def_1_id, expected_delta)
]
self.assertEqual(actual, expected_delta_specifications)
def testIngestApprovalDeltas_InvalidMask(self):
av_name = (
'projects/proj/issues/1/approvalValues/%d' % self.approval_def_1_id)
approval_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(name=av_name),
update_mask=field_mask_pb2.FieldMask(paths=['chicken']))
expected_err = 'Invalid `update_mask` for %s delta' % av_name
with self.assertRaisesRegexp(exceptions.InputException, expected_err):
self.converter.IngestApprovalDeltas([approval_delta], self.user_1.user_id)
def testIngestApprovalDeltas_FilterFieldValues(self):
av_name = (
'projects/proj/issues/1/approvalValues/%d' % self.approval_def_1_id)
# field_def_6 belongs to approval_def_1, should be ingested.
approval_fv = issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_6,
value=u'touch-nose',
derivation=RULE_DERIVATION, # Ignored.
)
# An enum field belonging to approval_def_1, should be ingested.
approval_enum_field_id = self._CreateFieldDef(
self.project_1.project_id,
'approval2field',
'ENUM_TYPE',
approval_id=self.approval_def_1_id)
approval_enum_fv = issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % approval_enum_field_id,
value=u'enumval')
# Create field value that points to different approval, should raise error.
approval_2_fv = issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_2, value=u'error')
av = issue_objects_pb2.ApprovalValue(
name=av_name, field_values=[approval_fv])
approval_delta = issues_pb2.ApprovalDelta(
update_mask=field_mask_pb2.FieldMask(paths=['field_values']),
approval_value=av,
field_vals_remove=[approval_enum_fv, approval_2_fv],
approvers_remove=['users/222'],
)
with self.assertRaisesRegexp(exceptions.InputException,
'Field .* does not belong to approval .*'):
self.converter.IngestApprovalDeltas([approval_delta], self.user_1.user_id)
def testIngestApprovalDeltas_InvalidFieldValues(self):
av_name = (
'projects/proj/issues/1/approvalValues/%d' % self.approval_def_1_id)
approval_fv = issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_6,
value=u'touch-nose',
derivation=RULE_DERIVATION, # Ignored.
)
other_fv = issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_1,
value=u'something',
)
# This does not exist, and should throw error.
dne_fv = issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/404',
value=u'DoesNotExist',
)
av = issue_objects_pb2.ApprovalValue(
name=av_name, field_values=[other_fv, approval_fv, dne_fv])
approval_delta = issues_pb2.ApprovalDelta(
update_mask=field_mask_pb2.FieldMask(paths=['field_values']),
approval_value=av,
approvers_remove=['users/222'],
)
with self.assertRaisesRegexp(
exceptions.InputException,
'Field projects/proj/fieldDefs/404 is not in this project'):
self.converter.IngestApprovalDeltas([approval_delta], self.user_1.user_id)
def testIngestApprovalDeltas_WrongProject(self):
approval_def_project2_name = 'project2_approval'
approval_def_project2_id = self._CreateFieldDef(
self.project_2.project_id,
approval_def_project2_name,
'APPROVAL_TYPE',
docstring='project2_ad_docstring',
admin_ids=[self.user_1.user_id])
self.services.config.UpdateConfig(
self.cnxn,
self.project_2,
approval_defs=[
(approval_def_project2_id, [self.user_1.user_id], 'survey')
])
wrong_project_av_name = (
'projects/proj/issues/1/approvalValues/%d' % approval_def_project2_id)
approval_delta = issues_pb2.ApprovalDelta(
update_mask=field_mask_pb2.FieldMask(),
approval_value=issue_objects_pb2.ApprovalValue(
name=wrong_project_av_name))
with self.assertRaises(exceptions.InputException):
self.converter.IngestApprovalDeltas([approval_delta], self.user_1.user_id)
def testIngestApprovalDeltas_DoesNotExist(self):
dne_av_name = ('projects/proj/issues/1/approvalValues/404')
approval_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(name=dne_av_name),
update_mask=field_mask_pb2.FieldMask())
with self.assertRaises(exceptions.InputException):
self.converter.IngestApprovalDeltas([approval_delta], self.user_1.user_id)
def testIngestApprovalDeltas_NonApproval(self):
"""We fail if provided a non-approval Field ID in the resource name."""
dne_av_name = (
'projects/proj/issues/1/approvalValues/%s' % self.field_def_1)
approval_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(name=dne_av_name),
update_mask=field_mask_pb2.FieldMask())
with self.assertRaises(exceptions.InputException):
self.converter.IngestApprovalDeltas([approval_delta], self.user_1.user_id)
def testIngestApprovalDeltas_IssueDoesNotExist(self):
dne_av_name = (
'projects/proj/issues/404/approvalValues/%d' % self.approval_def_1_id)
approval_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(name=dne_av_name),
update_mask=field_mask_pb2.FieldMask())
with self.assertRaises(exceptions.NoSuchIssueException):
self.converter.IngestApprovalDeltas([approval_delta], self.user_1.user_id)
def testIngestApprovalDeltas_EmptyDelta(self):
av_name = (
'projects/proj/issues/1/approvalValues/%d' % self.approval_def_1_id)
approval_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(name=av_name),
update_mask=field_mask_pb2.FieldMask())
actual = self.converter.IngestApprovalDeltas(
[approval_delta], self.user_1.user_id)
expected_delta = tracker_pb2.ApprovalDelta()
expected_delta_specifications = [
(self.issue_1.issue_id, self.approval_def_1_id, expected_delta)
]
self.assertEqual(actual, expected_delta_specifications)
def testIngestApprovalDeltas_InvalidName(self):
approval_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(name='x'))
with self.assertRaises(exceptions.InputException):
self.converter.IngestApprovalDeltas([approval_delta], self.user_1.user_id)
def testIngestApprovalDeltas_NoName(self):
approval_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(
status=issue_objects_pb2.ApprovalValue.ApprovalStatus.Value('NA')))
with self.assertRaises(exceptions.InputException):
self.converter.IngestApprovalDeltas([approval_delta], self.user_1.user_id)
def testIngestApprovalDeltas_NoStatus(self):
"""Setter ID isn't set when status isn't set."""
av_name = (
'projects/proj/issues/1/approvalValues/%d' % self.approval_def_1_id)
approval_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(
name=av_name,
status=issue_objects_pb2.ApprovalValue.ApprovalStatus.Value('NA'),
approvers=['users/333']),
# Status left out of update mask.
update_mask=field_mask_pb2.FieldMask(paths=['approvers']),
approvers_remove=['users/222'])
actual = self.converter.IngestApprovalDeltas(
[approval_delta], self.user_1.user_id)
expected_delta = tracker_pb2.ApprovalDelta(
approver_ids_add=[333], approver_ids_remove=[222])
expected_delta_specifications = [
(self.issue_1.issue_id, self.approval_def_1_id, expected_delta)
]
self.assertEqual(actual, expected_delta_specifications)
def testIngestApprovalDeltas_ApproverRemoveDoesNotExist(self):
av_name = (
'projects/proj/issues/1/approvalValues/%d' % self.approval_def_1_id)
approval_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(name=av_name),
update_mask=field_mask_pb2.FieldMask(),
approvers_remove=['users/nobody@404.com'])
with self.assertRaises(exceptions.NoSuchUserException):
self.converter.IngestApprovalDeltas([approval_delta], self.user_1.user_id)
def testIngestApprovalDeltas_ApproverAddDoesNotExist(self):
av_name = (
'projects/proj/issues/1/approvalValues/%d' % self.approval_def_1_id)
approval_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(
name=av_name, approvers=['users/nobody@404.com']),
update_mask=field_mask_pb2.FieldMask(paths=['approvers']))
with self.assertRaises(exceptions.NoSuchUserException):
self.converter.IngestApprovalDeltas([approval_delta], self.user_1.user_id)
def testIngestApprovalDeltas_FirstErrorRaised(self):
"""Until we have error aggregation, we raise the first found error."""
av_name = (
'projects/proj/issues/1/approvalValues/%d' % self.approval_def_1_id)
user_dne_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(
name=av_name, approvers=['users/nobody@404.com']),
update_mask=field_mask_pb2.FieldMask(paths=['approvers']))
invalid_name_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(name='garbage'))
with self.assertRaises(exceptions.NoSuchUserException):
self.converter.IngestApprovalDeltas(
[user_dne_delta, invalid_name_delta], self.user_1.user_id)
def testIngestApprovalDeltas_MultipleDeltasSameSetOn(self):
av_name = (
'projects/proj/issues/1/approvalValues/%d' % self.approval_def_1_id)
delta_1 = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(
name=av_name,
status=issue_objects_pb2.ApprovalValue.ApprovalStatus.Value('NA'),
approvers=['users/222']),
update_mask=field_mask_pb2.FieldMask(paths=['approvers', 'status']))
# Change status, and also ensure we don't reuse the same mask across deltas
# Approvers should be ignored for delta_2 because it is not included in the
# mask.
delta_2 = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(
name=av_name,
status=issue_objects_pb2.ApprovalValue.ApprovalStatus.Value(
'NOT_SET'),
approvers=['users/222']),
update_mask=field_mask_pb2.FieldMask(paths=['status']))
actual = self.converter.IngestApprovalDeltas(
[delta_1, delta_2], self.user_1.user_id)
self.assertEqual(len(actual), 2)
actual_iid_1, actual_approval_id_1, actual_delta_1 = actual[0]
actual_iid_2, actual_approval_id_2, actual_delta_2 = actual[1]
self.assertEqual(actual_iid_1, self.issue_1.issue_id)
self.assertEqual(actual_iid_2, self.issue_1.issue_id)
self.assertEqual(actual_approval_id_1, self.approval_def_1_id)
self.assertEqual(actual_approval_id_2, self.approval_def_1_id)
self.assertEqual(actual_delta_1.status, tracker_pb2.ApprovalStatus.NA)
self.assertEqual(actual_delta_2.status, tracker_pb2.ApprovalStatus.NOT_SET)
self.assertEqual(actual_delta_1.setter_id, self.user_1.user_id)
self.assertEqual(actual_delta_2.setter_id, self.user_1.user_id)
self.assertEqual(actual_delta_1.approver_ids_add, [222])
self.assertEqual(actual_delta_2.approver_ids_add, [])
# We don't patch time.time, so these would be different if the set_on wasn't
# passed in.
# Note: More ideal/correct unit test would create a mock that forces
# time.time to return an incremented value on its subsequent calls.
self.assertEqual(actual_delta_1.set_on, actual_delta_2.set_on)
def testIngestApprovalDeltas_DifferentProjects(self):
# Create an ApprovalDef for project2
approval_def_project2_name = 'project2_approval'
approval_def_project2_id = self._CreateFieldDef(
self.project_2.project_id,
approval_def_project2_name,
'APPROVAL_TYPE',
docstring='project2_ad_docstring',
admin_ids=[self.user_1.user_id])
self.services.config.UpdateConfig(
self.cnxn,
self.project_2,
approval_defs=[
(approval_def_project2_id, [self.user_1.user_id], 'survey')
])
# Define a field belonging to project_2's ApprovalDef.
project2_field_id = self._CreateFieldDef(
self.project_2.project_id,
'approval2field',
'STR_TYPE',
approval_id=approval_def_project2_id)
project2_fv = issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % project2_field_id, value=u'p2')
# field_def_6 belongs to approval_def_1.
project1_fv = issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_6,
value=u'touch-nose',
)
# Both ApprovalValues are provided both FieldValues, and we expect them
# to only include the FieldValues appropriate to their respective approvals.
project2_av_name = (
'projects/%s/issues/2/approvalValues/%d' %
(self.project_2.project_name, approval_def_project2_id))
project2_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(
name=project2_av_name, field_values=[project1_fv, project2_fv]),
update_mask=field_mask_pb2.FieldMask(paths=['field_values']))
project1_av_name = (
'projects/proj/issues/1/approvalValues/%d' % self.approval_def_1_id)
project1_delta = issues_pb2.ApprovalDelta(
approval_value=issue_objects_pb2.ApprovalValue(
name=project1_av_name, field_values=[project1_fv, project2_fv]),
update_mask=field_mask_pb2.FieldMask(paths=['field_values']))
with self.assertRaisesRegexp(
exceptions.InputException,
'Field projects/proj/fieldDefs/%d is not in this project' %
self.field_def_6):
self.converter.IngestApprovalDeltas(
[project2_delta, project1_delta], self.user_1.user_id)
def testIngestIssue(self):
ingest = issue_objects_pb2.Issue(
summary='sum',
status=issue_objects_pb2.Issue.StatusValue(
status='new', derivation=RULE_DERIVATION),
owner=issue_objects_pb2.Issue.UserValue(
derivation=EXPLICIT_DERIVATION, user='users/111'),
cc_users=[
issue_objects_pb2.Issue.UserValue(
derivation=EXPLICIT_DERIVATION, user='users/new@user.com'),
issue_objects_pb2.Issue.UserValue(
derivation=RULE_DERIVATION, user='users/333')
],
components=[
issue_objects_pb2.Issue.ComponentValue(
component='projects/proj/componentDefs/%d' %
self.component_def_1_id),
issue_objects_pb2.Issue.ComponentValue(
component='projects/proj/componentDefs/%d' %
self.component_def_2_id),
],
labels=[
issue_objects_pb2.Issue.LabelValue(
derivation=EXPLICIT_DERIVATION, label='a'),
issue_objects_pb2.Issue.LabelValue(
derivation=EXPLICIT_DERIVATION, label='key-explicit'),
issue_objects_pb2.Issue.LabelValue(
derivation=RULE_DERIVATION, label='derived1'),
issue_objects_pb2.Issue.LabelValue(
derivation=RULE_DERIVATION, label='key-derived')
],
field_values=[
issue_objects_pb2.FieldValue(
derivation=EXPLICIT_DERIVATION,
field='projects/proj/fieldDefs/%d' % self.field_def_1,
value='multivalue1',
),
issue_objects_pb2.FieldValue(
derivation=RULE_DERIVATION,
field='projects/proj/fieldDefs/%d' % self.field_def_1,
value='multivalue2',
),
issue_objects_pb2.FieldValue(
derivation=EXPLICIT_DERIVATION,
field='projects/proj/fieldDefs/%d' % self.field_def_3,
value='1',
),
issue_objects_pb2.FieldValue(
derivation=RULE_DERIVATION,
field='projects/proj/fieldDefs/%d' % self.field_def_4,
value='mac',
),
issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_2,
value='38', # Max value not checked.
),
issue_objects_pb2.FieldValue( # Multivalue not checked.
field='projects/proj/fieldDefs/%d' % self.field_def_2,
value='0' # Confirm we ingest 0 rather than None.
),
issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_8,
value='users/111',
),
issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_8,
value='users/404', # User lookup not attempted.
),
issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_9,
value='2020-01-01',
),
issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_9,
value='2100-01-01',
),
issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_9,
value='1000-01-01',
),
issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_10,
value='garbage',
),
],
merged_into_issue_ref=issue_objects_pb2.IssueRef(ext_identifier='b/1'),
blocked_on_issue_refs=[
# Reversing natural ordering to ensure order is respected.
issue_objects_pb2.IssueRef(issue='projects/goose/issues/4'),
issue_objects_pb2.IssueRef(issue='projects/proj/issues/3'),
issue_objects_pb2.IssueRef(ext_identifier='b/555'),
issue_objects_pb2.IssueRef(ext_identifier='b/2')
],
blocking_issue_refs=[
issue_objects_pb2.IssueRef(issue='projects/goose/issues/5'),
issue_objects_pb2.IssueRef(ext_identifier='b/3')
],
# All the following fields should be ignored.
name='projects/proj/issues/1',
state=issue_objects_pb2.IssueContentState.Value('SPAM'),
reporter='users/111',
create_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
component_modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
status_modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
owner_modify_time=timestamp_pb2.Timestamp(seconds=self.PAST_TIME),
star_count=1,
attachment_count=5,
phases=[self.phase_1.name])
blocked_on_1 = fake.MakeTestIssue(
self.project_1.project_id,
3,
'sum3',
'New',
self.user_1.user_id,
issue_id=301,
project_name=self.project_1.project_name,
)
blocked_on_2 = fake.MakeTestIssue(
self.project_2.project_id,
4,
'sum4',
'New',
self.user_1.user_id,
issue_id=401,
project_name=self.project_2.project_name,
)
blocking = fake.MakeTestIssue(
self.project_2.project_id,
5,
'sum5',
'New',
self.user_1.user_id,
issue_id=501,
project_name=self.project_2.project_name,
)
self.services.issue.TestAddIssue(blocked_on_1)
self.services.issue.TestAddIssue(blocked_on_2)
self.services.issue.TestAddIssue(blocking)
actual = self.converter.IngestIssue(ingest, self.project_1.project_id)
expected_cc1_id = self.services.user.LookupUserID(
self.cnxn, 'new@user.com', autocreate=False)
expected_field_values = [
tracker_pb2.FieldValue(
field_id=self.field_def_1,
str_value=u'multivalue1',
derived=False,
),
tracker_pb2.FieldValue(
field_id=self.field_def_1,
str_value=u'multivalue2',
derived=False,
),
tracker_pb2.FieldValue(
field_id=self.field_def_2, int_value=38, derived=False),
tracker_pb2.FieldValue(
field_id=self.field_def_2, int_value=0, derived=False),
tracker_pb2.FieldValue(
field_id=self.field_def_8, user_id=111, derived=False),
tracker_pb2.FieldValue(
field_id=self.field_def_8, user_id=404, derived=False),
tracker_pb2.FieldValue(
field_id=self.field_def_9, date_value=1577836800, derived=False),
tracker_pb2.FieldValue(
field_id=self.field_def_9, date_value=4102444800, derived=False),
tracker_pb2.FieldValue(
field_id=self.field_def_9, date_value=-30610224000, derived=False),
tracker_pb2.FieldValue(
field_id=self.field_def_10,
url_value=u'http://garbage',
derived=False),
]
expected = tracker_pb2.Issue(
project_id=self.project_1.project_id,
summary=u'sum',
status=u'new',
owner_id=111,
cc_ids=[expected_cc1_id, 333],
component_ids=[self.component_def_1_id, self.component_def_2_id],
merged_into_external=u'b/1',
labels=[
u'a', u'key-explicit', u'derived1', u'key-derived', u'days-1',
u'OS-mac'
],
field_values=expected_field_values,
blocked_on_iids=[blocked_on_2.issue_id, blocked_on_1.issue_id],
blocking_iids=[blocking.issue_id],
dangling_blocked_on_refs=[
tracker_pb2.DanglingIssueRef(ext_issue_identifier=u'b/555'),
tracker_pb2.DanglingIssueRef(ext_issue_identifier=u'b/2')
],
dangling_blocking_refs=[
tracker_pb2.DanglingIssueRef(ext_issue_identifier=u'b/3')
],
)
self.AssertProtosEqual(actual, expected)
def AssertProtosEqual(self, actual, expected):
"""Asserts equal, printing a diff if not."""
# TODO(jessan): If others find this useful, move to a shared testing lib.
try:
self.assertEqual(actual, expected)
except AssertionError as e:
# Append a diff to the normal error message.
expected_str = str(expected).splitlines(1)
actual_str = str(actual).splitlines(1)
diff = difflib.unified_diff(actual_str, expected_str)
err_msg = '%s\nProto actual vs expected diff:\n %s' % (e, ''.join(diff))
raise AssertionError(err_msg)
def testIngestIssue_Minimal(self):
"""Test IngestIssue with as few fields set as possible."""
minimal = issue_objects_pb2.Issue(
status=issue_objects_pb2.Issue.StatusValue(status='new')
)
expected = tracker_pb2.Issue(
project_id=self.project_1.project_id,
summary='', # Summary gets set to empty str on conversion.
status='new',
owner_id=0
)
actual = self.converter.IngestIssue(minimal, self.project_1.project_id)
self.assertEqual(actual, expected)
def testIngestIssue_NoSuchProject(self):
self.services.config.strict = True
ingest = issue_objects_pb2.Issue(
status=issue_objects_pb2.Issue.StatusValue(status='new'))
with self.assertRaises(exceptions.NoSuchProjectException):
self.converter.IngestIssue(ingest, -1)
def testIngestIssue_Errors(self):
invalid_issue_ref = issue_objects_pb2.IssueRef(
ext_identifier='b/1',
issue='projects/proj/issues/1')
ingest = issue_objects_pb2.Issue(
summary='sum',
owner=issue_objects_pb2.Issue.UserValue(
derivation=EXPLICIT_DERIVATION, user='users/nonexisting@user.com'),
cc_users=[
issue_objects_pb2.Issue.UserValue(
derivation=EXPLICIT_DERIVATION, user='invalidFormat1'),
issue_objects_pb2.Issue.UserValue(
derivation=RULE_DERIVATION, user='invalidFormat2')
],
components=[
issue_objects_pb2.Issue.ComponentValue(
component='projects/proj/componentDefs/404')
],
field_values=[
issue_objects_pb2.FieldValue(),
issue_objects_pb2.FieldValue(field='garbage'),
issue_objects_pb2.FieldValue(
field='projects/proj/fieldDefs/%d' % self.field_def_8,
value='users/nonexisting@user.com',
),
],
merged_into_issue_ref=invalid_issue_ref,
blocked_on_issue_refs=[
issue_objects_pb2.IssueRef(),
issue_objects_pb2.IssueRef(issue='projects/404/issues/1')
],
blocking_issue_refs=[
issue_objects_pb2.IssueRef(issue='projects/proj/issues/404')
],
)
error_messages = [
r'.+not found when ingesting owner',
r'.+cc_users: Invalid resource name: invalidFormat1.',
r'Status is required when creating an issue',
r'.+components: Component not found: 404.',
r'.+: Invalid resource name: .', r'.+: Invalid resource name: garbage.',
r'.+not found when ingesting user field:.+',
r'.+issue:.+[\n\r]+ext_identifier:.+[\n\r]+: IssueRefs MUST NOT have.+',
r'.+: IssueRefs MUST have one of.+',
r'.+issue:.+[\n\r]+: Project 404 not found.',
r'.+issue:.+[\n\r]+: Issue.+404.+not found'
]
error_messages_re = '\n'.join(error_messages)
with self.assertRaisesRegexp(exceptions.InputException, error_messages_re):
self.converter.IngestIssue(ingest, self.project_1.project_id)
def testIngestIssuesListColumns(self):
columns = [
issue_objects_pb2.IssuesListColumn(column='chicken'),
issue_objects_pb2.IssuesListColumn(column='boiled-egg')
]
self.assertEqual(
self.converter.IngestIssuesListColumns(columns), 'chicken boiled-egg')
def testIngestIssuesListColumns_Empty(self):
self.assertEqual(self.converter.IngestIssuesListColumns([]), '')
def test_ComputeIssuesListColumns(self):
"""Can convert string to sequence of IssuesListColumns"""
expected_columns = [
issue_objects_pb2.IssuesListColumn(column='chicken'),
issue_objects_pb2.IssuesListColumn(column='boiled-egg')
]
self.assertEqual(
expected_columns,
self.converter._ComputeIssuesListColumns('chicken boiled-egg'))
def test_ComputeIssuesListColumns_Empty(self):
"""Can handle empty strings"""
self.assertEqual([], self.converter._ComputeIssuesListColumns(''))
def test_Conversion_IssuesListColumns(self):
"""_Ingest and _Compute converts to and from each other"""
expected_columns = 'foo bar fizz buzz'
converted_columns = self.converter._ComputeIssuesListColumns(
expected_columns)
self.assertEqual(
expected_columns,
self.converter.IngestIssuesListColumns(converted_columns))
expected_columns = [
issue_objects_pb2.IssuesListColumn(column='foo'),
issue_objects_pb2.IssuesListColumn(column='bar'),
issue_objects_pb2.IssuesListColumn(column='fizz'),
issue_objects_pb2.IssuesListColumn(column='buzz')
]
converted_columns = self.converter.IngestIssuesListColumns(expected_columns)
self.assertEqual(
expected_columns,
self.converter._ComputeIssuesListColumns(converted_columns))
def testIngestNotifyType(self):
notify = issues_pb2.NotifyType.Value('NOTIFY_TYPE_UNSPECIFIED')
actual = self.converter.IngestNotifyType(notify)
self.assertEqual(actual, True)
notify = issues_pb2.NotifyType.Value('EMAIL')
actual = self.converter.IngestNotifyType(notify)
self.assertEqual(actual, True)
notify = issues_pb2.NotifyType.Value('NO_NOTIFICATION')
actual = self.converter.IngestNotifyType(notify)
self.assertEqual(actual, False)
def test_GetNonApprovalFieldValues(self):
"""It filters out field values that belong to approvals"""
expected_str = 'some_string_field_value'
fv_expected = fake.MakeFieldValue(
field_id=self.field_def_1, str_value=expected_str, derived=False)
actual = self.converter._GetNonApprovalFieldValues(
[fv_expected, self.fv_6], self.project_1.project_id)
self.assertEqual(len(actual), 1)
self.assertEqual(actual[0], fv_expected)
def test_GetNonApprovalFieldValues_Empty(self):
actual = self.converter._GetNonApprovalFieldValues(
[], self.project_1.project_id)
self.assertEqual(actual, [])
def testConvertFieldValues(self):
"""It ignores field values referencing a non-existent field"""
expected_str = 'some_string_field_value'
fv = fake.MakeFieldValue(
field_id=self.field_def_1, str_value=expected_str, derived=False)
expected_name = rnc.ConvertFieldDefNames(
self.cnxn, [self.field_def_1], self.project_1.project_id,
self.services)[self.field_def_1]
expected_value = issue_objects_pb2.FieldValue(
field=expected_name,
value=expected_str,
derivation=EXPLICIT_DERIVATION,
phase=None)
output = self.converter.ConvertFieldValues(
[fv], self.project_1.project_id, [])
self.assertEqual([expected_value], output)
def testConvertFieldValues_Empty(self):
output = self.converter.ConvertFieldValues(
[], self.project_1.project_id, [])
self.assertEqual([], output)
def testConvertFieldValues_PreservesOrder(self):
"""It ignores field values referencing a non-existent field"""
expected_str = 'some_string_field_value'
fv_1 = fake.MakeFieldValue(
field_id=self.field_def_1, str_value=expected_str, derived=False)
name_1 = rnc.ConvertFieldDefNames(
self.cnxn, [self.field_def_1], self.project_1.project_id,
self.services)[self.field_def_1]
expected_1 = issue_objects_pb2.FieldValue(
field=name_1,
value=expected_str,
derivation=EXPLICIT_DERIVATION,
phase=None)
expected_int = 111111
fv_2 = fake.MakeFieldValue(
field_id=self.field_def_2, int_value=expected_int, derived=True)
name_2 = rnc.ConvertFieldDefNames(
self.cnxn, [self.field_def_2], self.project_1.project_id,
self.services).get(self.field_def_2)
expected_2 = issue_objects_pb2.FieldValue(
field=name_2,
value=str(expected_int),
derivation=RULE_DERIVATION,
phase=None)
output = self.converter.ConvertFieldValues(
[fv_1, fv_2], self.project_1.project_id, [])
self.assertEqual([expected_1, expected_2], output)
def testConvertFieldValues_IgnoresNullFieldDefs(self):
"""It ignores field values referencing a non-existent field"""
expected_str = 'some_string_field_value'
fv_1 = fake.MakeFieldValue(
field_id=self.field_def_1, str_value=expected_str, derived=False)
name_1 = rnc.ConvertFieldDefNames(
self.cnxn, [self.field_def_1], self.project_1.project_id,
self.services)[self.field_def_1]
expected_1 = issue_objects_pb2.FieldValue(
field=name_1,
value=expected_str,
derivation=EXPLICIT_DERIVATION,
phase=None)
fv_2 = fake.MakeFieldValue(
field_id=self.dne_field_def_id, int_value=111111, derived=True)
output = self.converter.ConvertFieldValues(
[fv_1, fv_2], self.project_1.project_id, [])
self.assertEqual([expected_1], output)
def test_ComputeFieldValueString_None(self):
with self.assertRaises(exceptions.InputException):
self.converter._ComputeFieldValueString(None)
def test_ComputeFieldValueString_INT_TYPE(self):
expected = 123158
fv = fake.MakeFieldValue(field_id=self.field_def_2, int_value=expected)
output = self.converter._ComputeFieldValueString(fv)
self.assertEqual(str(expected), output)
def test_ComputeFieldValueString_STR_TYPE(self):
expected = 'some_string_field_value'
fv = fake.MakeFieldValue(field_id=self.field_def_1, str_value=expected)
output = self.converter._ComputeFieldValueString(fv)
self.assertEqual(expected, output)
def test_ComputeFieldValueString_USER_TYPE(self):
user_id = self.user_1.user_id
expected = rnc.ConvertUserName(user_id)
fv = fake.MakeFieldValue(field_id=self.dne_field_def_id, user_id=user_id)
output = self.converter._ComputeFieldValueString(fv)
self.assertEqual(expected, output)
def test_ComputeFieldValueString_DATE_TYPE(self):
expected = 1234567890
fv = fake.MakeFieldValue(
field_id=self.dne_field_def_id, date_value=expected)
output = self.converter._ComputeFieldValueString(fv)
self.assertEqual(str(expected), output)
def test_ComputeFieldValueString_URL_TYPE(self):
expected = 'some URL'
fv = fake.MakeFieldValue(field_id=self.dne_field_def_id, url_value=expected)
output = self.converter._ComputeFieldValueString(fv)
self.assertEqual(expected, output)
def test_ComputeFieldValueDerivation_RULE(self):
expected = RULE_DERIVATION
fv = fake.MakeFieldValue(
field_id=self.field_def_1, str_value='something', derived=True)
output = self.converter._ComputeFieldValueDerivation(fv)
self.assertEqual(expected, output)
def test_ComputeFieldValueDerivation_EXPLICIT(self):
expected = EXPLICIT_DERIVATION
fv = fake.MakeFieldValue(
field_id=self.field_def_1, str_value='something', derived=False)
output = self.converter._ComputeFieldValueDerivation(fv)
self.assertEqual(expected, output)
def testConvertApprovalValues_Issue(self):
"""We can convert issue approval_values."""
name = rnc.ConvertApprovalValueNames(
self.cnxn, self.issue_1.issue_id, self.services)[self.av_1.approval_id]
approval_def_name = rnc.ConvertApprovalDefNames(
self.cnxn, [self.approval_def_1_id], self.project_1.project_id,
self.services)[self.approval_def_1_id]
approvers = [rnc.ConvertUserName(self.user_2.user_id)]
status = issue_objects_pb2.ApprovalValue.ApprovalStatus.Value(
'NOT_SET')
setter = rnc.ConvertUserName(self.user_1.user_id)
api_fvs = self.converter.ConvertFieldValues(
[self.fv_6], self.project_1.project_id, [self.phase_1])
# Check we can handle converting a None `set_on`.
self.av_1.set_on = None
output = self.converter.ConvertApprovalValues(
[self.av_1], [self.fv_1, self.fv_6], [self.phase_1],
issue_id=self.issue_1.issue_id)
expected = issue_objects_pb2.ApprovalValue(
name=name,
approval_def=approval_def_name,
approvers=approvers,
status=status,
setter=setter,
phase=self.phase_1.name,
field_values=api_fvs)
self.assertEqual([expected], output)
def testConvertApprovalValues_Templates(self):
"""We can convert template approval_values."""
approval_def_name = rnc.ConvertApprovalDefNames(
self.cnxn, [self.approval_def_1_id], self.project_1.project_id,
self.services)[self.approval_def_1_id]
approvers = [rnc.ConvertUserName(self.user_2.user_id)]
status = issue_objects_pb2.ApprovalValue.ApprovalStatus.Value(
'NOT_SET')
set_time = timestamp_pb2.Timestamp()
set_time.FromSeconds(self.PAST_TIME)
setter = rnc.ConvertUserName(self.user_1.user_id)
api_fvs = self.converter.ConvertFieldValues(
[self.fv_6], self.project_1.project_id, [self.phase_1])
output = self.converter.ConvertApprovalValues(
[self.av_1], [self.fv_1, self.fv_6], [self.phase_1],
project_id=self.project_1.project_id)
expected = issue_objects_pb2.ApprovalValue(
approval_def=approval_def_name,
approvers=approvers,
status=status,
set_time=set_time,
setter=setter,
phase=self.phase_1.name,
field_values=api_fvs)
self.assertEqual([expected], output)
def testConvertApprovalValues_NoPhase(self):
approval_def_name = rnc.ConvertApprovalDefNames(
self.cnxn, [self.approval_def_1_id], self.project_1.project_id,
self.services)[self.approval_def_1_id]
approvers = [rnc.ConvertUserName(self.user_2.user_id)]
status = issue_objects_pb2.ApprovalValue.ApprovalStatus.Value(
'NOT_SET')
set_time = timestamp_pb2.Timestamp()
set_time.FromSeconds(self.PAST_TIME)
setter = rnc.ConvertUserName(self.user_1.user_id)
expected = issue_objects_pb2.ApprovalValue(
approval_def=approval_def_name,
approvers=approvers,
status=status,
set_time=set_time,
setter=setter)
output = self.converter.ConvertApprovalValues(
[self.av_1], [], [], project_id=self.project_1.project_id)
self.assertEqual([expected], output)
def testConvertApprovalValues_Empty(self):
output = self.converter.ConvertApprovalValues(
[], [], [], project_id=self.project_1.project_id)
self.assertEqual([], output)
def testConvertApprovalValues_IgnoresNullFieldDefs(self):
"""It ignores approval values referencing a non-existent field"""
av = fake.MakeApprovalValue(self.dne_field_def_id)
output = self.converter.ConvertApprovalValues(
[av], [], [], issue_id=self.issue_1.issue_id)
self.assertEqual([], output)
def test_ComputeApprovalValueStatus_NOT_SET(self):
self.assertEqual(
self.converter._ComputeApprovalValueStatus(
tracker_pb2.ApprovalStatus.NOT_SET),
issue_objects_pb2.ApprovalValue.ApprovalStatus.Value(
'NOT_SET'))
def test_ComputeApprovalValueStatus_NEEDS_REVIEW(self):
self.assertEqual(
self.converter._ComputeApprovalValueStatus(
tracker_pb2.ApprovalStatus.NEEDS_REVIEW),
issue_objects_pb2.ApprovalValue.ApprovalStatus.Value('NEEDS_REVIEW'))
def test_ComputeApprovalValueStatus_NA(self):
self.assertEqual(
self.converter._ComputeApprovalValueStatus(
tracker_pb2.ApprovalStatus.NA),
issue_objects_pb2.ApprovalValue.ApprovalStatus.Value('NA'))
def test_ComputeApprovalValueStatus_REVIEW_REQUESTED(self):
self.assertEqual(
self.converter._ComputeApprovalValueStatus(
tracker_pb2.ApprovalStatus.REVIEW_REQUESTED),
issue_objects_pb2.ApprovalValue.ApprovalStatus.Value(
'REVIEW_REQUESTED'))
def test_ComputeApprovalValueStatus_REVIEW_STARTED(self):
self.assertEqual(
self.converter._ComputeApprovalValueStatus(
tracker_pb2.ApprovalStatus.REVIEW_STARTED),
issue_objects_pb2.ApprovalValue.ApprovalStatus.Value('REVIEW_STARTED'))
def test_ComputeApprovalValueStatus_NEED_INFO(self):
self.assertEqual(
self.converter._ComputeApprovalValueStatus(
tracker_pb2.ApprovalStatus.NEED_INFO),
issue_objects_pb2.ApprovalValue.ApprovalStatus.Value('NEED_INFO'))
def test_ComputeApprovalValueStatus_APPROVED(self):
self.assertEqual(
self.converter._ComputeApprovalValueStatus(
tracker_pb2.ApprovalStatus.APPROVED),
issue_objects_pb2.ApprovalValue.ApprovalStatus.Value('APPROVED'))
def test_ComputeApprovalValueStatus_NOT_APPROVED(self):
self.assertEqual(
self.converter._ComputeApprovalValueStatus(
tracker_pb2.ApprovalStatus.NOT_APPROVED),
issue_objects_pb2.ApprovalValue.ApprovalStatus.Value('NOT_APPROVED'))
def test_ComputeTemplatePrivacy_PUBLIC(self):
self.assertEqual(
self.converter._ComputeTemplatePrivacy(self.template_1),
project_objects_pb2.IssueTemplate.TemplatePrivacy.Value('PUBLIC'))
def test_ComputeTemplatePrivacy_MEMBERS_ONLY(self):
self.assertEqual(
self.converter._ComputeTemplatePrivacy(self.template_2),
project_objects_pb2.IssueTemplate.TemplatePrivacy.Value('MEMBERS_ONLY'))
def test_ComputeTemplateDefaultOwner_UNSPECIFIED(self):
self.assertEqual(
self.converter._ComputeTemplateDefaultOwner(self.template_1),
project_objects_pb2.IssueTemplate.DefaultOwner.Value(
'DEFAULT_OWNER_UNSPECIFIED'))
def test_ComputeTemplateDefaultOwner_REPORTER(self):
self.assertEqual(
self.converter._ComputeTemplateDefaultOwner(self.template_2),
project_objects_pb2.IssueTemplate.DefaultOwner.Value(
'PROJECT_MEMBER_REPORTER'))
def test_ComputePhases(self):
"""It sorts by rank"""
phase1 = fake.MakePhase(123111, name='phase1name', rank=3)
phase2 = fake.MakePhase(123112, name='phase2name', rank=2)
phase3 = fake.MakePhase(123113, name='phase3name', rank=1)
expected = ['phase3name', 'phase2name', 'phase1name']
self.assertEqual(
self.converter._ComputePhases([phase1, phase2, phase3]), expected)
def test_ComputePhases_EMPTY(self):
self.assertEqual(self.converter._ComputePhases([]), [])
def test_FillIssueFromTemplate(self):
result = self.converter._FillIssueFromTemplate(
self.template_1, self.project_1.project_id)
self.assertFalse(result.name)
self.assertEqual(result.summary, self.template_1.summary)
self.assertEqual(
result.state, issue_objects_pb2.IssueContentState.Value('ACTIVE'))
self.assertEqual(result.status.status, 'New')
self.assertFalse(result.reporter)
self.assertEqual(result.owner.user, 'users/{}'.format(self.user_1.user_id))
self.assertEqual(len(result.cc_users), 0)
self.assertFalse(result.cc_users)
self.assertEqual(len(result.labels), 1)
self.assertEqual(result.labels[0].label, self.template_1.labels[0])
self.assertEqual(result.labels[0].derivation, EXPLICIT_DERIVATION)
self.assertEqual(len(result.components), 1)
self.assertEqual(
result.components[0].component, 'projects/{}/componentDefs/{}'.format(
self.project_1.project_name, self.template_1.component_ids[0]))
self.assertEqual(result.components[0].derivation, EXPLICIT_DERIVATION)
self.assertEqual(len(result.field_values), 2)
self.assertEqual(
result.field_values[0].field, 'projects/{}/fieldDefs/{}'.format(
self.project_1.project_name, self.field_def_1))
self.assertEqual(result.field_values[0].value, self.fv_1_value)
self.assertEqual(result.field_values[0].derivation, EXPLICIT_DERIVATION)
expected_name = rnc.ConvertFieldDefNames(
self.cnxn, [self.field_def_3], self.project_1.project_id,
self.services).get(self.field_def_3)
self.assertEqual(
result.field_values[1],
issue_objects_pb2.FieldValue(
field=expected_name,
value=self.template_1_label1_value,
derivation=EXPLICIT_DERIVATION))
self.assertFalse(result.blocked_on_issue_refs)
self.assertFalse(result.blocking_issue_refs)
self.assertFalse(result.attachment_count)
self.assertFalse(result.star_count)
self.assertEqual(len(result.phases), 1)
self.assertEqual(result.phases[0], self.phase_1.name)
def test_FillIssueFromTemplate_NoPhase(self):
result = self.converter._FillIssueFromTemplate(
self.template_3, self.project_1.project_id)
self.assertEqual(len(result.field_values), 1)
self.assertEqual(
result.field_values[0].field, 'projects/{}/fieldDefs/{}'.format(
self.project_1.project_name, self.field_def_1))
self.assertEqual(result.field_values[0].value, self.fv_1_value)
self.assertEqual(result.field_values[0].derivation, EXPLICIT_DERIVATION)
self.assertEqual(len(result.phases), 0)
def test_FillIssueFromTemplate_FilterApprovalFV(self):
template = self.services.template.TestAddIssueTemplateDef(
11114,
self.project_1.project_id,
'template3',
field_values=[self.fv_1, self.fv_6],
approval_values=[self.av_2],
)
result = self.converter._FillIssueFromTemplate(
template, self.project_1.project_id)
self.assertEqual(len(result.field_values), 1)
self.assertEqual(
result.field_values[0].field, 'projects/{}/fieldDefs/{}'.format(
self.project_1.project_name, self.field_def_1))
self.assertEqual(result.field_values[0].value, self.fv_1_value)
self.assertEqual(result.field_values[0].derivation, EXPLICIT_DERIVATION)
def testConvertIssueTemplates(self):
result = self.converter.ConvertIssueTemplates(
self.project_1.project_id, [self.template_1])
self.assertEqual(len(result), 1)
actual = result[0]
self.assertEqual(
actual.name, 'projects/{}/templates/{}'.format(
self.project_1.project_name, self.template_1.template_id))
self.assertEqual(actual.display_name, self.template_1.name)
self.assertEqual(actual.summary_must_be_edited, False)
self.assertEqual(
actual.template_privacy,
project_objects_pb2.IssueTemplate.TemplatePrivacy.Value('PUBLIC'))
self.assertEqual(
actual.default_owner,
project_objects_pb2.IssueTemplate.DefaultOwner.Value(
'DEFAULT_OWNER_UNSPECIFIED'))
self.assertEqual(actual.component_required, False)
self.assertEqual(actual.admins, ['users/{}'.format(self.user_2.user_id)])
self.assertEqual(
actual.issue,
self.converter._FillIssueFromTemplate(
self.template_1, self.project_1.project_id))
self.assertListEqual(
[av for av in actual.approval_values],
self.converter.ConvertApprovalValues(
self.template_1.approval_values, self.template_1.field_values,
self.template_1.phases, project_id=self.project_1.project_id))
def testConvertIssueTemplates_IgnoresNonExistentTemplate(self):
result = self.converter.ConvertIssueTemplates(
self.project_1.project_id, [self.dne_template])
self.assertEqual(len(result), 0)
def testConvertLabels_OmitsFieldDefs(self):
"""It omits field def labels"""
input_labels = ['pri-1', '{}-2'.format(self.field_def_3_name)]
result = self.converter.ConvertLabels(
input_labels, [], self.project_1.project_id)
self.assertEqual(len(result), 1)
expected = issue_objects_pb2.Issue.LabelValue(
label=input_labels[0], derivation=EXPLICIT_DERIVATION)
self.assertEqual(result[0], expected)
def testConvertLabels_DerivedLabels(self):
"""It handles derived labels"""
input_labels = ['pri-1']
result = self.converter.ConvertLabels(
[], input_labels, self.project_1.project_id)
self.assertEqual(len(result), 1)
expected = issue_objects_pb2.Issue.LabelValue(
label=input_labels[0], derivation=RULE_DERIVATION)
self.assertEqual(result[0], expected)
def testConvertLabels(self):
"""It includes both non-derived and derived labels"""
input_labels = ['pri-1', '{}-2'.format(self.field_def_3_name)]
input_der_labels = ['{}-3'.format(self.field_def_3_name), 'job-secret']
result = self.converter.ConvertLabels(
input_labels, input_der_labels, self.project_1.project_id)
self.assertEqual(len(result), 2)
expected_0 = issue_objects_pb2.Issue.LabelValue(
label=input_labels[0], derivation=EXPLICIT_DERIVATION)
self.assertEqual(result[0], expected_0)
expected_1 = issue_objects_pb2.Issue.LabelValue(
label=input_der_labels[1], derivation=RULE_DERIVATION)
self.assertEqual(result[1], expected_1)
def testConvertLabels_Empty(self):
result = self.converter.ConvertLabels([], [], self.project_1.project_id)
self.assertEqual(result, [])
def testConvertEnumFieldValues_OnlyFieldDefs(self):
"""It only returns enum field values"""
expected_value = '2'
input_labels = [
'pri-1', '{}-{}'.format(self.field_def_3_name, expected_value)
]
result = self.converter.ConvertEnumFieldValues(
input_labels, [], self.project_1.project_id)
self.assertEqual(len(result), 1)
expected_name = rnc.ConvertFieldDefNames(
self.cnxn, [self.field_def_3], self.project_1.project_id,
self.services).get(self.field_def_3)
expected = issue_objects_pb2.FieldValue(
field=expected_name,
value=expected_value,
derivation=EXPLICIT_DERIVATION)
self.assertEqual(result[0], expected)
def testConvertEnumFieldValues_DerivedLabels(self):
"""It handles derived enum field values"""
expected_value = '2'
input_der_labels = [
'pri-1', '{}-{}'.format(self.field_def_3_name, expected_value)
]
result = self.converter.ConvertEnumFieldValues(
[], input_der_labels, self.project_1.project_id)
self.assertEqual(len(result), 1)
expected_name = rnc.ConvertFieldDefNames(
self.cnxn, [self.field_def_3], self.project_1.project_id,
self.services).get(self.field_def_3)
expected = issue_objects_pb2.FieldValue(
field=expected_name, value=expected_value, derivation=RULE_DERIVATION)
self.assertEqual(result[0], expected)
def testConvertEnumFieldValues_Empty(self):
result = self.converter.ConvertEnumFieldValues(
[], [], self.project_1.project_id)
self.assertEqual(result, [])
def testConvertEnumFieldValues_ProjectSpecific(self):
"""It only considers field defs from specified project"""
expected_value = '2'
input_labels = [
'{}-{}'.format(self.field_def_3_name, expected_value),
'{}-ipsum'.format(self.field_def_project2_name)
]
result = self.converter.ConvertEnumFieldValues(
input_labels, [], self.project_1.project_id)
self.assertEqual(len(result), 1)
expected_name = rnc.ConvertFieldDefNames(
self.cnxn, [self.field_def_3], self.project_1.project_id,
self.services).get(self.field_def_3)
expected = issue_objects_pb2.FieldValue(
field=expected_name,
value=expected_value,
derivation=EXPLICIT_DERIVATION)
self.assertEqual(result[0], expected)
def testConvertEnumFieldValues(self):
"""It handles derived enum field values"""
expected_value_0 = '2'
expected_value_1 = 'macOS'
input_labels = [
'pri-1', '{}-{}'.format(self.field_def_3_name, expected_value_0),
'{}-ipsum'.format(self.field_def_project2_name)
]
input_der_labels = [
'{}-{}'.format(self.field_def_4_name, expected_value_1), 'foo-bar'
]
result = self.converter.ConvertEnumFieldValues(
input_labels, input_der_labels, self.project_1.project_id)
self.assertEqual(len(result), 2)
expected_0_name = rnc.ConvertFieldDefNames(
self.cnxn, [self.field_def_3], self.project_1.project_id,
self.services).get(self.field_def_3)
expected_0 = issue_objects_pb2.FieldValue(
field=expected_0_name,
value=expected_value_0,
derivation=EXPLICIT_DERIVATION)
self.assertEqual(result[0], expected_0)
expected_1_name = rnc.ConvertFieldDefNames(
self.cnxn, [self.field_def_4], self.project_1.project_id,
self.services).get(self.field_def_4)
expected_1 = issue_objects_pb2.FieldValue(
field=expected_1_name,
value=expected_value_1,
derivation=RULE_DERIVATION)
self.assertEqual(result[1], expected_1)
@mock.patch('project.project_helpers.GetThumbnailUrl')
def testConvertProject(self, mock_GetThumbnailUrl):
"""We can convert a Project."""
mock_GetThumbnailUrl.return_value = 'xyz'
expected_api_project = project_objects_pb2.Project(
name='projects/{}'.format(self.project_1.project_name),
display_name=self.project_1.project_name,
summary=self.project_1.summary,
thumbnail_url='xyz')
self.assertEqual(
expected_api_project, self.converter.ConvertProject(self.project_1))
@mock.patch('project.project_helpers.GetThumbnailUrl')
def testConvertProjects(self, mock_GetThumbnailUrl):
"""We can convert a Sequence of Projects."""
mock_GetThumbnailUrl.return_value = 'xyz'
expected_api_projects = [
project_objects_pb2.Project(
name='projects/{}'.format(self.project_1.project_name),
display_name=self.project_1.project_name,
summary=self.project_1.summary,
thumbnail_url='xyz'),
project_objects_pb2.Project(
name='projects/{}'.format(self.project_2.project_name),
display_name=self.project_2.project_name,
summary=self.project_2.summary,
thumbnail_url='xyz')
]
self.assertEqual(
expected_api_projects,
self.converter.ConvertProjects([self.project_1, self.project_2]))
def testConvertProjectConfig(self):
"""We can convert a project_config"""
project_config = self.services.config.GetProjectConfig(
self.cnxn, self.project_1.project_id)
expected_grid_config = project_objects_pb2.ProjectConfig.GridViewConfig(
default_x_attr=project_config.default_x_attr,
default_y_attr=project_config.default_y_attr)
template_names = rnc.ConvertTemplateNames(
self.cnxn, project_config.project_id, [
project_config.default_template_for_developers,
project_config.default_template_for_users
], self.services)
expected_api_config = project_objects_pb2.ProjectConfig(
name=rnc.ConvertProjectConfigName(
self.cnxn, self.project_1.project_id, self.services),
exclusive_label_prefixes=project_config.exclusive_label_prefixes,
member_default_query=project_config.member_default_query,
default_sort=project_config.default_sort_spec,
default_columns=[
issue_objects_pb2.IssuesListColumn(column=col)
for col in project_config.default_col_spec.split()
],
project_grid_config=expected_grid_config,
member_default_template=template_names.get(
project_config.default_template_for_developers),
non_members_default_template=template_names.get(
project_config.default_template_for_users),
revision_url_format=self.project_1.revision_url_format,
custom_issue_entry_url=project_config.custom_issue_entry_url)
self.converter.user_auth = authdata.AuthData.FromUser(
self.cnxn, self.user_1, self.services)
self.assertEqual(
expected_api_config,
self.converter.ConvertProjectConfig(project_config))
def testConvertProjectConfig_NonMembers(self):
"""We can convert a project_config for non project members"""
self.converter.user_auth = authdata.AuthData.FromUser(
self.cnxn, self.user_2, self.services)
project_config = self.services.config.GetProjectConfig(
self.cnxn, self.project_1.project_id)
api_config = self.converter.ConvertProjectConfig(project_config)
expected_default_query = project_config.member_default_query
self.assertEqual(expected_default_query, api_config.member_default_query)
expected_member_default_template = rnc.ConvertTemplateNames(
self.cnxn, project_config.project_id,
[project_config.default_template_for_developers], self.services).get(
project_config.default_template_for_developers)
self.assertEqual(
expected_member_default_template, api_config.member_default_template)
def testCreateProjectMember(self):
"""We can create a ProjectMember."""
expected_project_member = project_objects_pb2.ProjectMember(
name='projects/proj/members/111',
role=project_objects_pb2.ProjectMember.ProjectRole.Value('OWNER'))
self.assertEqual(
expected_project_member,
self.converter.CreateProjectMember(self.cnxn, 789, 111, 'OWNER'))
def test_ConvertDateAction(self):
"""We can convert from protorpc to protoc FieldDef.DateAction"""
date_type_settings = project_objects_pb2.FieldDef.DateTypeSettings
input_type = tracker_pb2.DateAction.NO_ACTION
actual = self.converter._ConvertDateAction(input_type)
expected = date_type_settings.DateAction.Value('NO_ACTION')
self.assertEqual(expected, actual)
input_type = tracker_pb2.DateAction.PING_OWNER_ONLY
actual = self.converter._ConvertDateAction(input_type)
expected = date_type_settings.DateAction.Value('NOTIFY_OWNER')
self.assertEqual(expected, actual)
input_type = tracker_pb2.DateAction.PING_PARTICIPANTS
actual = self.converter._ConvertDateAction(input_type)
expected = date_type_settings.DateAction.Value('NOTIFY_PARTICIPANTS')
self.assertEqual(expected, actual)
def test_ConvertRoleRequirements(self):
"""We can convert from protorpc to protoc FieldDef.RoleRequirements"""
user_type_settings = project_objects_pb2.FieldDef.UserTypeSettings
actual = self.converter._ConvertRoleRequirements(False)
expected = user_type_settings.RoleRequirements.Value('NO_ROLE_REQUIREMENT')
self.assertEqual(expected, actual)
actual = self.converter._ConvertRoleRequirements(True)
expected = user_type_settings.RoleRequirements.Value('PROJECT_MEMBER')
self.assertEqual(expected, actual)
def test_ConvertNotifyTriggers(self):
"""We can convert from protorpc to protoc FieldDef.NotifyTriggers"""
user_type_settings = project_objects_pb2.FieldDef.UserTypeSettings
input_type = tracker_pb2.NotifyTriggers.NEVER
actual = self.converter._ConvertNotifyTriggers(input_type)
expected = user_type_settings.NotifyTriggers.Value('NEVER')
self.assertEqual(expected, actual)
input_type = tracker_pb2.NotifyTriggers.ANY_COMMENT
actual = self.converter._ConvertNotifyTriggers(input_type)
expected = user_type_settings.NotifyTriggers.Value('ANY_COMMENT')
self.assertEqual(expected, actual)
def test_ConvertFieldDefType(self):
"""We can convert from protorpc FieldType to protoc FieldDef.Type"""
input_type = tracker_pb2.FieldTypes.ENUM_TYPE
actual = self.converter._ConvertFieldDefType(input_type)
expected = project_objects_pb2.FieldDef.Type.Value('ENUM')
self.assertEqual(expected, actual)
input_type = tracker_pb2.FieldTypes.INT_TYPE
actual = self.converter._ConvertFieldDefType(input_type)
expected = project_objects_pb2.FieldDef.Type.Value('INT')
self.assertEqual(expected, actual)
input_type = tracker_pb2.FieldTypes.STR_TYPE
actual = self.converter._ConvertFieldDefType(input_type)
expected = project_objects_pb2.FieldDef.Type.Value('STR')
self.assertEqual(expected, actual)
input_type = tracker_pb2.FieldTypes.USER_TYPE
actual = self.converter._ConvertFieldDefType(input_type)
expected = project_objects_pb2.FieldDef.Type.Value('USER')
self.assertEqual(expected, actual)
input_type = tracker_pb2.FieldTypes.DATE_TYPE
actual = self.converter._ConvertFieldDefType(input_type)
expected = project_objects_pb2.FieldDef.Type.Value('DATE')
self.assertEqual(expected, actual)
input_type = tracker_pb2.FieldTypes.URL_TYPE
actual = self.converter._ConvertFieldDefType(input_type)
expected = project_objects_pb2.FieldDef.Type.Value('URL')
self.assertEqual(expected, actual)
def test_ConvertFieldDefType_BOOL(self):
"""We raise exception for unsupported input type BOOL"""
input_type = tracker_pb2.FieldTypes.BOOL_TYPE
with self.assertRaises(ValueError) as cm:
self.converter._ConvertFieldDefType(input_type)
self.assertEqual(
'Unsupported tracker_pb2.FieldType enum. Boolean types '
'are unsupported and approval types are found in ApprovalDefs',
str(cm.exception))
def test_ConvertFieldDefType_APPROVAL(self):
"""We raise exception for input type APPROVAL"""
input_type = tracker_pb2.FieldTypes.APPROVAL_TYPE
with self.assertRaises(ValueError) as cm:
self.converter._ConvertFieldDefType(input_type)
self.assertEqual(
'Unsupported tracker_pb2.FieldType enum. Boolean types '
'are unsupported and approval types are found in ApprovalDefs',
str(cm.exception))
def testConvertFieldDefs(self):
"""We can convert field defs"""
project_config = self.services.config.GetProjectConfig(
self.cnxn, self.project_1.project_id)
input_fds = project_config.field_defs
output = self.converter.ConvertFieldDefs(
input_fds, self.project_1.project_id)
fd1_rn = rnc.ConvertFieldDefNames(
self.cnxn, [self.field_def_1], self.project_1.project_id,
self.services).get(self.field_def_1)
self.assertEqual(fd1_rn, output[0].name)
self.assertEqual(self.field_def_1_name, output[0].display_name)
self.assertEqual('', output[0].docstring)
self.assertEqual(
project_objects_pb2.FieldDef.Type.Value('STR'), output[0].type)
self.assertEqual(
project_objects_pb2.FieldDef.Type.Value('INT'), output[1].type)
self.assertEqual('', output[1].applicable_issue_type)
fd1_admin_editor = [rnc.ConvertUserName(self.user_1.user_id)]
self.assertEqual(fd1_admin_editor, output[0].admins)
self.assertEqual(fd1_admin_editor, output[5].editors)
def testConvertFieldDefs_Traits(self):
"""We can convert FieldDefs with traits"""
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_1)
output = self.converter.ConvertFieldDefs(
[input_fd], self.project_1.project_id)
self.assertEqual(1, len(output))
expected_traits = [
project_objects_pb2.FieldDef.Traits.Value('REQUIRED'),
project_objects_pb2.FieldDef.Traits.Value('MULTIVALUED'),
project_objects_pb2.FieldDef.Traits.Value('PHASE')
]
self.assertEqual(expected_traits, output[0].traits)
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_2)
output = self.converter.ConvertFieldDefs(
[input_fd], self.project_1.project_id)
self.assertEqual(1, len(output))
expected_traits = [
project_objects_pb2.FieldDef.Traits.Value('DEFAULT_HIDDEN')
]
self.assertEqual(expected_traits, output[0].traits)
def testConvertFieldDefs_ApprovalParent(self):
"""We can convert FieldDef with approval parents"""
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_6)
output = self.converter.ConvertFieldDefs(
[input_fd], self.project_1.project_id)
self.assertEqual(1, len(output))
approval_names_dict = rnc.ConvertApprovalDefNames(
self.cnxn, [self.approval_def_1_id], self.project_1.project_id,
self.services)
expected_approval_parent = approval_names_dict.get(input_fd.approval_id)
self.assertEqual(expected_approval_parent, output[0].approval_parent)
def testConvertFieldDefs_EnumTypeSettings(self):
"""We can convert enum FieldDef and its settings"""
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_5)
output = self.converter.ConvertFieldDefs(
[input_fd], self.project_1.project_id)
self.assertEqual(1, len(output))
expected_settings = project_objects_pb2.FieldDef.EnumTypeSettings(
choices=[
Choice(
value='submarine', docstring=self.labeldef_2.label_docstring),
Choice(value='basket', docstring=self.labeldef_3.label_docstring)
])
self.assertEqual(expected_settings, output[0].enum_settings)
def testConvertFieldDefs_IntTypeSettings(self):
"""We can convert int FieldDef and its settings"""
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_2)
output = self.converter.ConvertFieldDefs(
[input_fd], self.project_1.project_id)
self.assertEqual(1, len(output))
expected_settings = project_objects_pb2.FieldDef.IntTypeSettings(
max_value=37)
self.assertEqual(expected_settings, output[0].int_settings)
def testConvertFieldDefs_StrTypeSettings(self):
"""We can convert str FieldDef and its settings"""
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_1)
output = self.converter.ConvertFieldDefs(
[input_fd], self.project_1.project_id)
self.assertEqual(1, len(output))
expected_settings = project_objects_pb2.FieldDef.StrTypeSettings(
regex='abc')
self.assertEqual(expected_settings, output[0].str_settings)
def testConvertFieldDefs_UserTypeSettings(self):
"""We can convert user FieldDef and its settings"""
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_8)
output = self.converter.ConvertFieldDefs(
[input_fd], self.project_1.project_id)
self.assertEqual(1, len(output))
user_settings = project_objects_pb2.FieldDef.UserTypeSettings
expected_settings = project_objects_pb2.FieldDef.UserTypeSettings(
role_requirements=user_settings.RoleRequirements.Value(
'PROJECT_MEMBER'),
needs_perm='EDIT_PROJECT',
notify_triggers=user_settings.NotifyTriggers.Value('ANY_COMMENT'))
self.assertEqual(expected_settings, output[0].user_settings)
def testConvertFieldDefs_DateTypeSettings(self):
"""We can convert user FieldDef and its settings"""
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_9)
output = self.converter.ConvertFieldDefs(
[input_fd], self.project_1.project_id)
self.assertEqual(1, len(output))
date_settings = project_objects_pb2.FieldDef.DateTypeSettings
expected_settings = project_objects_pb2.FieldDef.DateTypeSettings(
date_action=date_settings.DateAction.Value('NOTIFY_OWNER'))
self.assertEqual(expected_settings, output[0].date_settings)
def testConvertFieldDefs_SkipsApprovals(self):
"""We skip over approval defs"""
project_config = self.services.config.GetProjectConfig(
self.cnxn, self.project_1.project_id)
input_fds = project_config.field_defs
# project_1 is set up to have 10 non-approval fields and 2 approval fields.
self.assertEqual(12, len(input_fds))
output = self.converter.ConvertFieldDefs(
input_fds, self.project_1.project_id)
# assert we skip approval fields
self.assertEqual(10, len(output))
def testConvertFieldDefs_NonexistentID(self):
"""We skip over any field defs whose ID does not exist."""
input_fd = tracker_pb2.FieldDef(
field_id=self.dne_field_def_id,
project_id=self.project_1.project_id,
field_name='foobar',
field_type=tracker_pb2.FieldTypes('STR_TYPE'))
output = self.converter.ConvertFieldDefs(
[input_fd], self.project_1.project_id)
self.assertEqual(0, len(output))
def testConvertFieldDefs_Empty(self):
"""We can handle empty list input"""
self.assertEqual(
[], self.converter.ConvertFieldDefs([], self.project_1.project_id))
def test_ComputeFieldDefTraits(self):
"""We can get Sequence of Traits for a FieldDef"""
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_1)
actual = self.converter._ComputeFieldDefTraits(input_fd)
expected = [
project_objects_pb2.FieldDef.Traits.Value('REQUIRED'),
project_objects_pb2.FieldDef.Traits.Value('MULTIVALUED'),
project_objects_pb2.FieldDef.Traits.Value('PHASE')
]
self.assertEqual(expected, actual)
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_2)
actual = self.converter._ComputeFieldDefTraits(input_fd)
expected = [project_objects_pb2.FieldDef.Traits.Value('DEFAULT_HIDDEN')]
self.assertEqual(expected, actual)
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_7)
actual = self.converter._ComputeFieldDefTraits(input_fd)
expected = [project_objects_pb2.FieldDef.Traits.Value('RESTRICTED')]
self.assertEqual(expected, actual)
def test_ComputeFieldDefTraits_Empty(self):
"""We return an empty Sequence of Traits for plain FieldDef"""
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_3)
actual = self.converter._ComputeFieldDefTraits(input_fd)
self.assertEqual([], actual)
def test_GetEnumFieldChoices(self):
"""We can get all choices for an enum field"""
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_5)
actual = self.converter._GetEnumFieldChoices(input_fd)
expected = [
Choice(
value=self.labeldef_2.label.split('-')[1],
docstring=self.labeldef_2.label_docstring),
Choice(
value=self.labeldef_3.label.split('-')[1],
docstring=self.labeldef_3.label_docstring),
]
self.assertEqual(expected, actual)
def test_GetEnumFieldChoices_NotEnumField(self):
"""We raise exception for non-enum-field"""
input_fd = self._GetFieldDefById(
self.project_1.project_id, self.field_def_1)
with self.assertRaises(ValueError) as cm:
self.converter._GetEnumFieldChoices(input_fd)
self.assertEqual(
'Cannot get value from label for non-enum-type field', str(
cm.exception))
def testConvertApprovalDefs(self):
"""We can convert ApprovalDefs"""
input_ad = self._GetApprovalDefById(
self.project_1.project_id, self.approval_def_1_id)
actual = self.converter.ConvertApprovalDefs(
[input_ad], self.project_1.project_id)
resource_names_dict = rnc.ConvertApprovalDefNames(
self.cnxn, [self.approval_def_1_id], self.project_1.project_id,
self.services)
expected_name = resource_names_dict.get(self.approval_def_1_id)
self.assertEqual(actual[0].name, expected_name)
self.assertEqual(actual[0].display_name, self.approval_def_1_name)
matching_fd = self._GetFieldDefById(
self.project_1.project_id, self.approval_def_1_id)
expected_docstring = matching_fd.docstring
self.assertEqual(actual[0].docstring, expected_docstring)
self.assertEqual(actual[0].survey, self.approval_def_1.survey)
expected_approvers = [rnc.ConvertUserName(self.user_2.user_id)]
self.assertEqual(actual[0].approvers, expected_approvers)
expected_admins = [rnc.ConvertUserName(self.user_1.user_id)]
self.assertEqual(actual[0].admins, expected_admins)
def testConvertApprovalDefs_Empty(self):
"""We can handle empty case"""
actual = self.converter.ConvertApprovalDefs([], self.project_1.project_id)
self.assertEqual(actual, [])
def testConvertApprovalDefs_SkipsNonApprovalDefs(self):
"""We skip if no matching field def exists"""
input_ad = tracker_pb2.ApprovalDef(
approval_id=self.dne_field_def_id,
approver_ids=[self.user_2.user_id],
survey='anything goes')
actual = self.converter.ConvertApprovalDefs(
[input_ad], self.project_1.project_id)
self.assertEqual(actual, [])
def testConvertLabelDefs(self):
"""We can convert LabelDefs"""
actual = self.converter.ConvertLabelDefs(
[self.labeldef_1, self.labeldef_5], self.project_1.project_id)
resource_names_dict = rnc.ConvertLabelDefNames(
self.cnxn, [self.labeldef_1.label, self.labeldef_5.label],
self.project_1.project_id, self.services)
expected_0_name = resource_names_dict.get(self.labeldef_1.label)
expected_0 = project_objects_pb2.LabelDef(
name=expected_0_name,
value=self.labeldef_1.label,
docstring=self.labeldef_1.label_docstring,
state=project_objects_pb2.LabelDef.LabelDefState.Value('ACTIVE'))
self.assertEqual(expected_0, actual[0])
expected_1_name = resource_names_dict.get(self.labeldef_5.label)
expected_1 = project_objects_pb2.LabelDef(
name=expected_1_name,
value=self.labeldef_5.label,
docstring=self.labeldef_5.label_docstring,
state=project_objects_pb2.LabelDef.LabelDefState.Value('DEPRECATED'))
self.assertEqual(expected_1, actual[1])
def testConvertLabelDefs_Empty(self):
"""We can handle empty input case"""
actual = self.converter.ConvertLabelDefs([], self.project_1.project_id)
self.assertEqual([], actual)
def testConvertStatusDefs(self):
"""We can convert StatusDefs"""
actual = self.converter.ConvertStatusDefs(
self.predefined_statuses, self.project_1.project_id)
self.assertEqual(len(actual), 4)
input_names = [sd.status for sd in self.predefined_statuses]
names = rnc.ConvertStatusDefNames(
self.cnxn, input_names, self.project_1.project_id, self.services)
self.assertEqual(names[self.status_1.status], actual[0].name)
self.assertEqual(names[self.status_2.status], actual[1].name)
self.assertEqual(names[self.status_3.status], actual[2].name)
self.assertEqual(names[self.status_4.status], actual[3].name)
self.assertEqual(self.status_1.status, actual[0].value)
self.assertEqual(
project_objects_pb2.StatusDef.StatusDefType.Value('OPEN'),
actual[0].type)
self.assertEqual(0, actual[0].rank)
self.assertEqual(self.status_1.status_docstring, actual[0].docstring)
self.assertEqual(
project_objects_pb2.StatusDef.StatusDefState.Value('ACTIVE'),
actual[0].state)
def testConvertStatusDefs_Empty(self):
"""Can handle empty input case"""
actual = self.converter.ConvertStatusDefs([], self.project_1.project_id)
self.assertEqual([], actual)
def testConvertStatusDefs_Rank(self):
"""Rank is indepdendent of input order"""
input_sds = [self.status_2, self.status_4, self.status_3, self.status_1]
actual = self.converter.ConvertStatusDefs(
input_sds, self.project_1.project_id)
self.assertEqual(1, actual[0].rank)
self.assertEqual(3, actual[1].rank)
def testConvertStatusDefs_type_MERGED(self):
"""Includes mergeable status when parsed from project config"""
actual = self.converter.ConvertStatusDefs(
[self.status_2], self.project_1.project_id)
self.assertEqual(
project_objects_pb2.StatusDef.StatusDefType.Value('MERGED'),
actual[0].type)
def testConvertStatusDefs_state_DEPRECATED(self):
"""Includes deprecated status"""
actual = self.converter.ConvertStatusDefs(
[self.status_4], self.project_1.project_id)
self.assertEqual(
project_objects_pb2.StatusDef.StatusDefState.Value('DEPRECATED'),
actual[0].state)
def testConvertComponentDef(self):
now = 123
project = self.services.project.TestAddProject('comp-test', project_id=987)
config = fake.MakeTestConfig(project.project_id, [], [])
component_def = fake.MakeTestComponentDef(
project.project_id, 1, path='Chickens>Dickens')
component_def.created = now
config.component_defs = [component_def]
self.services.config.StoreConfig(self.cnxn, config)
actual = self.converter.ConvertComponentDef(component_def)
expected = project_objects_pb2.ComponentDef(
name='projects/comp-test/componentDefs/1',
value='Chickens>Dickens',
state=project_objects_pb2.ComponentDef.ComponentDefState.Value(
'ACTIVE'),
create_time=timestamp_pb2.Timestamp(seconds=now),
modify_time=timestamp_pb2.Timestamp())
self.assertEqual(actual, expected)
def testConvertComponentDefs(self):
"""We can convert ComponentDefs"""
project_config = self.services.config.GetProjectConfig(
self.cnxn, self.project_1.project_id)
self.assertEqual(len(project_config.component_defs), 2)
actual = self.converter.ConvertComponentDefs(
project_config.component_defs, self.project_1.project_id)
self.assertEqual(2, len(actual))
resource_names_dict = rnc.ConvertComponentDefNames(
self.cnxn, [self.component_def_1_id, self.component_def_2_id],
self.project_1.project_id, self.services)
self.assertEqual(
resource_names_dict.get(self.component_def_1_id), actual[0].name)
self.assertEqual(
resource_names_dict.get(self.component_def_2_id), actual[1].name)
self.assertEqual(self.component_def_1_path, actual[0].value)
self.assertEqual(self.component_def_2_path, actual[1].value)
self.assertEqual('cd1_docstring', actual[0].docstring)
self.assertEqual(
project_objects_pb2.ComponentDef.ComponentDefState.Value('ACTIVE'),
actual[0].state)
self.assertEqual(
project_objects_pb2.ComponentDef.ComponentDefState.Value('DEPRECATED'),
actual[1].state)
# component_def 1 and 2 have the same admins, ccs, creator, and create_time
expected_admins = [rnc.ConvertUserName(self.user_1.user_id)]
self.assertEqual(expected_admins, actual[0].admins)
expected_ccs = [rnc.ConvertUserName(self.user_2.user_id)]
self.assertEqual(expected_ccs, actual[0].ccs)
expected_creator = rnc.ConvertUserName(self.user_1.user_id)
self.assertEqual(expected_creator, actual[0].creator)
expected_create_time = timestamp_pb2.Timestamp(seconds=self.PAST_TIME)
self.assertEqual(expected_create_time, actual[0].create_time)
expected_labels = [ld.label for ld in self.predefined_labels]
self.assertEqual(expected_labels, actual[0].labels)
self.assertEqual([], actual[1].labels)
def testConvertComponentDefs_Empty(self):
"""Can handle empty input case"""
actual = self.converter.ConvertComponentDefs([], self.project_1.project_id)
self.assertEqual([], actual)
def testConvertProjectSavedQueries(self):
"""We can convert ProjectSavedQueries"""
input_psqs = [self.psq_2]
actual = self.converter.ConvertProjectSavedQueries(
input_psqs, self.project_1.project_id)
self.assertEqual(1, len(actual))
resource_names_dict = rnc.ConvertProjectSavedQueryNames(
self.cnxn, [self.psq_2.query_id], self.project_1.project_id,
self.services)
self.assertEqual(
resource_names_dict.get(self.psq_2.query_id), actual[0].name)
self.assertEqual(self.psq_2.name, actual[0].display_name)
self.assertEqual(self.psq_2.query, actual[0].query)
def testConvertProjectSavedQueries_ExpandsBasedOn(self):
"""We expand query to include base_query_id"""
actual = self.converter.ConvertProjectSavedQueries(
[self.psq_1], self.project_1.project_id)
expected_query = '{} {}'.format(
tbo.GetBuiltInQuery(self.psq_1.base_query_id), self.psq_1.query)
self.assertEqual(expected_query, actual[0].query)
def testConvertProjectSavedQueries_NotInProject(self):
"""We skip over saved queries that don't belong to this project"""
psq_not_registered = tracker_pb2.SavedQuery(
query_id=4, name='psq no registered name', query='no registered')
actual = self.converter.ConvertProjectSavedQueries(
[psq_not_registered], self.project_1.project_id)
self.assertEqual([], actual)
def testConvertProjectSavedQueries_Empty(self):
"""We can handle empty inputs"""
actual = self.converter.ConvertProjectSavedQueries(
[], self.project_1.project_id)
self.assertEqual([], actual)