blob: b18f70bd63ba2da5953f78713f6fcedda5c03537 [file] [log] [blame]
# Copyright 2016 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unittests for the issueentry servlet."""
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
try:
from mox3 import mox
except ImportError:
import mox
import time
import unittest
import ezt
from google.appengine.ext import testbed
from mock import Mock, patch
from framework import framework_bizobj
from framework import framework_views
from framework import permissions
from services import service_manager
from services import template_svc
from testing import fake
from testing import testing_helpers
from tracker import issueentry
from tracker import tracker_bizobj
from mrproto import tracker_pb2
from mrproto import user_pb2
class IssueEntryTest(unittest.TestCase):
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
self.testbed.init_memcache_stub()
self.testbed.init_datastore_v3_stub()
# Load queue.yaml.
self.services = service_manager.Services(
config=fake.ConfigService(),
issue=fake.IssueService(),
user=fake.UserService(),
usergroup=fake.UserGroupService(),
project=fake.ProjectService(),
template=Mock(spec=template_svc.TemplateService),
features=fake.FeaturesService())
self.project = self.services.project.TestAddProject('proj', project_id=987)
self.servlet = issueentry.IssueEntry(services=self.services)
self.user = self.services.user.TestAddUser('to_pass_tests', 0)
self.services.features.TestAddHotlist(
name='dontcare', summary='', owner_ids=[0])
self.template = testing_helpers.DefaultTemplates()[1]
self.services.template.GetTemplateByName = Mock(return_value=self.template)
self.services.template.GetTemplateSetForProject = Mock(
return_value=[(1, 'name', False)])
# Set-up for testing hotlist parsing.
# Scenario:
# Users: U1, U2, and U3
# Hotlists:
# H1: owned by U1 (private)
# H2: owned by U2, can be edited by U1 (private)
# H2: owned by U3, can be edited by U1 and U2 (public)
self.cnxn = fake.MonorailConnection()
self.U1 = self.services.user.TestAddUser('U1', 111)
self.U2 = self.services.user.TestAddUser('U2', 222)
self.U3 = self.services.user.TestAddUser('U3', 333)
self.H1 = self.services.features.TestAddHotlist(
name='H1', summary='', owner_ids=[111], is_private=True)
self.H2 = self.services.features.TestAddHotlist(
name='H2', summary='', owner_ids=[222], editor_ids=[111],
is_private=True)
self.H2_U3 = self.services.features.TestAddHotlist(
name='H2', summary='', owner_ids=[333], editor_ids=[111, 222],
is_private=False)
self.mox = mox.Mox()
def tearDown(self):
self.testbed.deactivate()
self.mox.UnsetStubs()
self.mox.ResetAll()
def testAssertBasePermission(self):
"""Permit users with CREATE_ISSUE."""
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services,
perms=permissions.EMPTY_PERMISSIONSET)
self.assertRaises(permissions.PermissionException,
self.servlet.AssertBasePermission, mr)
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services,
perms=permissions.CONTRIBUTOR_ACTIVE_PERMISSIONSET)
self.servlet.AssertBasePermission(mr)
def testDiscardUnusedTemplateLabelPrefixes(self):
labels = ['pre-val', 'other-value', 'oneword', 'x', '-y', '-w-z', '', '-']
self.assertEqual(labels,
issueentry._DiscardUnusedTemplateLabelPrefixes(labels))
labels = ['prefix-value', 'other-?', 'third-', '', '-', '-?']
self.assertEqual(['prefix-value', 'third-', '', '-'],
issueentry._DiscardUnusedTemplateLabelPrefixes(labels))
def testGatherPageData(self):
user = self.services.user.TestAddUser('user@invalid', 100)
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services)
mr.perms = permissions.PermissionSet(
[permissions.CREATE_ISSUE, permissions.EDIT_ISSUE])
mr.auth.user_view = framework_views.MakeUserView(
'cnxn', self.services.user, 100)
mr.auth.effective_ids = {100}
mr.template_name = 'rutabaga'
self.mox.StubOutWithMock(self.services.user, 'GetUser')
self.services.user.GetUser(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(user)
self.mox.ReplayAll()
config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
config.field_defs = [
tracker_bizobj.MakeFieldDef(
22, mr.project_id, 'NotEnum', tracker_pb2.FieldTypes.STR_TYPE, None,
'', False, False, False, None, None, '', False, '', '',
tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'doc', False),
tracker_bizobj.MakeFieldDef(
23, mr.project_id, 'Choices', tracker_pb2.FieldTypes.ENUM_TYPE,
None, '', False, False, False, None, None, '', False, '', '',
tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'doc', False),
tracker_bizobj.MakeFieldDef(
24,
mr.project_id,
'RestrictedField',
tracker_pb2.FieldTypes.STR_TYPE,
None,
'',
False,
False,
False,
None,
None,
'',
False,
'',
'',
tracker_pb2.NotifyTriggers.NEVER,
'no_action',
'doc',
False,
is_restricted_field=True)
]
self.services.config.StoreConfig(mr.cnxn, config)
template = tracker_pb2.TemplateDef(
labels=['NotEnum-Not-Masked', 'Choices-Masked'])
self.services.template.GetTemplateByName.return_value = template
page_data = self.servlet.GatherPageData(mr)
self.mox.VerifyAll()
self.assertEqual(page_data['initial_owner'], 'user@invalid')
self.assertEqual(page_data['initial_status'], 'New')
self.assertTrue(page_data['clear_summary_on_click'])
self.assertTrue(page_data['must_edit_summary'])
self.assertEqual(page_data['labels'], ['NotEnum-Not-Masked'])
self.assertEqual(page_data['offer_templates'], ezt.boolean(False))
self.assertEqual(page_data['fields'][0].is_editable, ezt.boolean(True))
self.assertEqual(page_data['fields'][1].is_editable, ezt.boolean(True))
self.assertEqual(page_data['fields'][2].is_editable, ezt.boolean(False))
self.assertEqual(page_data['uneditable_fields'], ezt.boolean(True))
def testGatherPageData_Approvals(self):
user = self.services.user.TestAddUser('user@invalid', 100)
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services)
mr.auth.user_view = framework_views.MakeUserView(
'cnxn', self.services.user, 100)
mr.template_name = 'rutabaga'
self.mox.StubOutWithMock(self.services.user, 'GetUser')
self.services.user.GetUser(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(user)
self.mox.ReplayAll()
config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
config.field_defs = [
tracker_bizobj.MakeFieldDef(
24, mr.project_id, 'UXReview',
tracker_pb2.FieldTypes.APPROVAL_TYPE, None, '', False, False,
False, None, None, '', False, '', '',
tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'doc', False)]
self.services.config.StoreConfig(mr.cnxn, config)
template = tracker_pb2.TemplateDef()
template.phases = [tracker_pb2.Phase(
phase_id=1, rank=4, name='Stable')]
template.approval_values = [tracker_pb2.ApprovalValue(
approval_id=24, phase_id=1,
status=tracker_pb2.ApprovalStatus.NEEDS_REVIEW)]
self.services.template.GetTemplateByName.return_value = template
page_data = self.servlet.GatherPageData(mr)
self.mox.VerifyAll()
self.assertEqual(page_data['approvals'][0].field_name, 'UXReview')
self.assertEqual(page_data['initial_phases'][0],
tracker_pb2.Phase(phase_id=1, name='Stable', rank=4))
self.assertEqual(page_data['prechecked_approvals'], ['24_phase_0'])
self.assertEqual(page_data['required_approval_ids'], [24])
# phase fields row shown when config contains phase fields.
config.field_defs.append(tracker_bizobj.MakeFieldDef(
26, mr.project_id, 'GateTarget',
tracker_pb2.FieldTypes.INT_TYPE, None, '', False, False, False,
None, None, '', False, '', '', tracker_pb2.NotifyTriggers.NEVER,
'no_action', 'doc', False, is_phase_field=True))
self.services.config.StoreConfig(mr.cnxn, config)
page_data = self.servlet.GatherPageData(mr)
self.assertEqual(page_data['issue_phase_names'], ['stable'])
# approval subfields in config hidden when chosen template does not contain
# its parent approval
template = tracker_pb2.TemplateDef()
self.services.template.GetTemplateByName.return_value = template
page_data = self.servlet.GatherPageData(mr)
self.assertEqual(page_data['approvals'], [])
# phase fields row hidden when template has no phases
self.assertEqual(page_data['issue_phase_names'], [])
# TODO(jojwang): monorail:6305, remove this test when Edit perms
# for field values are implemented.
def testGatherPageData_FLTSpecialFields(self):
user = self.services.user.TestAddUser('user@invalid', 100)
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services)
mr.auth.user_view = framework_views.MakeUserView(
'cnxn', self.services.user, 100)
mr.template_name = 'rutabaga'
self.mox.StubOutWithMock(self.services.user, 'GetUser')
self.services.user.GetUser(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(user)
self.mox.ReplayAll()
config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
config.field_defs = [
tracker_bizobj.MakeFieldDef(
25, mr.project_id, 'nOtice',
tracker_pb2.FieldTypes.STR_TYPE, None, '', False, False,
False, None, None, '', False, '', '',
tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'doc', False),
tracker_bizobj.MakeFieldDef(
24, mr.project_id, 'M-Target',
tracker_pb2.FieldTypes.STR_TYPE, None, '', False, False,
False, None, None, '', False, '', '',
tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'doc', False),
tracker_bizobj.MakeFieldDef(
25, mr.project_id, 'whitepaper',
tracker_pb2.FieldTypes.STR_TYPE, None, '', False, False,
False, None, None, '', False, '', '',
tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'doc', False),
tracker_bizobj.MakeFieldDef(
25, mr.project_id, 'm-approved',
tracker_pb2.FieldTypes.STR_TYPE, None, '', False, False,
False, None, None, '', False, '', '',
tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'doc', False),
]
self.services.config.StoreConfig(mr.cnxn, config)
template = tracker_pb2.TemplateDef()
self.services.template.GetTemplateByName.return_value = template
page_data = self.servlet.GatherPageData(mr)
self.mox.VerifyAll()
self.assertEqual(page_data['fields'][0].field_name, 'M-Target')
self.assertEqual(len(page_data['fields']), 1)
def testGatherPageData_DefaultOwnerAvailability(self):
user = self.services.user.TestAddUser('user@invalid', 100)
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services)
mr.auth.user_view = framework_views.MakeUserView(
'cnxn', self.services.user, 100)
mr.template_name = 'rutabaga'
self.mox.StubOutWithMock(self.services.user, 'GetUser')
self.services.user.GetUser(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(user)
self.mox.ReplayAll()
page_data = self.servlet.GatherPageData(mr)
self.mox.VerifyAll()
self.assertEqual(page_data['initial_owner'], 'user@invalid')
self.assertEqual(page_data['owner_avail_state'], 'never')
self.assertEqual(
page_data['owner_avail_message_short'],
'User never visited')
user.last_visit_timestamp = int(time.time())
mr.auth.user_view = framework_views.MakeUserView(
'cnxn', self.services.user, 100)
page_data = self.servlet.GatherPageData(mr)
self.mox.VerifyAll()
self.assertEqual(page_data['initial_owner'], 'user@invalid')
self.assertEqual(page_data['owner_avail_state'], None)
self.assertEqual(page_data['owner_avail_message_short'], '')
def testGatherPageData_TemplateAllowsKeepingSummary(self):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services)
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
mr.template_name = 'rutabaga'
user = self.services.user.TestAddUser('user@invalid', 100)
self.mox.StubOutWithMock(self.services.user, 'GetUser')
self.services.user.GetUser(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(user)
self.mox.ReplayAll()
config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
self.services.config.StoreConfig(mr.cnxn, config)
self.template.summary_must_be_edited = False
page_data = self.servlet.GatherPageData(mr)
self.mox.VerifyAll()
self.assertEqual(page_data['initial_owner'], 'user@invalid')
self.assertEqual(page_data['initial_status'], 'New')
self.assertFalse(page_data['clear_summary_on_click'])
self.assertFalse(page_data['must_edit_summary'])
def testGatherPageData_DeepLinkSetsSummary(self):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry?summary=foo', services=self.services)
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
user = self.services.user.TestAddUser('user@invalid', 100)
mr.template_name = 'rutabaga'
self.mox.StubOutWithMock(self.services.user, 'GetUser')
self.services.user.GetUser(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(user)
self.mox.ReplayAll()
page_data = self.servlet.GatherPageData(mr)
self.mox.VerifyAll()
self.assertEqual(page_data['initial_owner'], 'user@invalid')
self.assertEqual(page_data['initial_status'], 'New')
self.assertFalse(page_data['clear_summary_on_click'])
self.assertTrue(page_data['must_edit_summary'])
@patch('framework.framework_bizobj.UserIsInProject')
def testGatherPageData_MembersOnlyTemplatesExcluded(self,
mockUserIsInProject):
"""Templates with members_only=True are excluded from results
when the user is not a member of the project."""
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services)
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
user = self.services.user.TestAddUser('user@invalid', 100)
mr.template_name = 'rutabaga'
self.services.template.GetTemplateSetForProject = Mock(
return_value=[(1, 'one', False), (2, 'two', True)])
mockUserIsInProject.return_value = False
self.mox.StubOutWithMock(self.services.user, 'GetUser')
self.services.user.GetUser(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(user)
self.mox.ReplayAll()
page_data = self.servlet.GatherPageData(mr)
self.mox.VerifyAll()
self.assertEqual(page_data['config'].template_names, ['one'])
@patch('framework.framework_bizobj.UserIsInProject')
def testGatherPageData_DefaultTemplatesMember(self, mockUserIsInProject):
"""If no template is specified, the default one is used based on
whether the user is a project member."""
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services)
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
user = self.services.user.TestAddUser('user@invalid', 100)
self.services.template.GetTemplateSetForProject = Mock(
return_value=[(1, 'one', False), (2, 'two', True)])
config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
config.default_template_for_users = 456
config.default_template_for_developers = 789
self.services.config.StoreConfig(mr.cnxn, config)
mockUserIsInProject.return_value = True
self.services.template.GetTemplateById = Mock(return_value=self.template)
self.mox.StubOutWithMock(self.services.user, 'GetUser')
self.services.user.GetUser(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(user)
self.mox.ReplayAll()
self.servlet.GatherPageData(mr)
self.mox.VerifyAll()
call_args = self.services.template.GetTemplateById.call_args[0]
self.assertEqual(call_args[1], 789)
@patch('framework.framework_bizobj.UserIsInProject')
def testGatherPageData_DefaultTemplatesNonMember(self, mockUserIsInProject):
"""If no template is specified, the default one is used based on
whether the user is not a project member."""
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services)
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
user = self.services.user.TestAddUser('user@invalid', 100)
self.services.template.GetTemplateSetForProject = Mock(
return_value=[(1, 'one', False), (2, 'two', True)])
config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
config.default_template_for_users = 456
config.default_template_for_developers = 789
self.services.config.StoreConfig(mr.cnxn, config)
mockUserIsInProject.return_value = False
self.services.template.GetTemplateById = Mock(return_value=self.template)
self.mox.StubOutWithMock(self.services.user, 'GetUser')
self.services.user.GetUser(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(user)
self.mox.ReplayAll()
self.servlet.GatherPageData(mr)
self.mox.VerifyAll()
call_args = self.services.template.GetTemplateById.call_args[0]
self.assertEqual(call_args[1], 456)
def testGatherPageData_MissingDefaultTemplates(self):
"""If the default templates were deleted, pick the first template."""
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services)
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
user = self.services.user.TestAddUser('user@invalid', 100)
self.services.template.GetTemplateSetForProject = Mock(
return_value=[(1, 'one', False), (2, 'two', True)])
self.services.template.GetTemplateById.return_value = None
self.services.template.GetProjectTemplates.return_value = [
tracker_pb2.TemplateDef(members_only=True),
tracker_pb2.TemplateDef(members_only=False)]
self.mox.StubOutWithMock(self.services.user, 'GetUser')
self.services.user.GetUser(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(user)
self.mox.ReplayAll()
page_data = self.servlet.GatherPageData(mr)
self.mox.VerifyAll()
self.assertTrue(self.services.template.GetProjectTemplates.called)
self.assertTrue(page_data['config'].template_view.members_only)
def testGatherPageData_IncorrectTemplate(self):
"""The handler shouldn't error out if passed a non-existent template."""
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services)
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
mr.template_name = 'rutabaga'
user = self.services.user.TestAddUser('user@invalid', 100)
config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
config.default_template_for_users = 456
config.default_template_for_developers = 789
self.services.config.StoreConfig(mr.cnxn, config)
self.services.template.GetTemplateSetForProject.return_value = [
(1, 'one', False), (2, 'two', True)]
self.services.template.GetTemplateByName.return_value = None
self.services.template.GetTemplateById.return_value = \
tracker_pb2.TemplateDef(template_id=123, labels=['yo'])
self.services.template.GetProjectTemplates.return_value = [
tracker_pb2.TemplateDef(labels=['no']),
tracker_pb2.TemplateDef(labels=['maybe'])]
self.mox.StubOutWithMock(self.services.user, 'GetUser')
self.services.user.GetUser(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(user)
self.mox.ReplayAll()
page_data = self.servlet.GatherPageData(mr)
self.mox.VerifyAll()
self.assertTrue(self.services.template.GetTemplateByName.called)
self.assertTrue(self.services.template.GetTemplateById.called)
self.assertFalse(self.services.template.GetProjectTemplates.called)
self.assertEqual(page_data['config'].template_view.label0, 'yo')
def testGatherPageData_RestrictNewIssues(self):
"""Users with this pref set default to reporting issues with R-V-G."""
self.mox.ReplayAll()
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', services=self.services)
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
user = self.services.user.TestAddUser('user@invalid', 100)
self.services.user.GetUser = Mock(return_value=user)
self.services.template.GetTemplateById = Mock(return_value=self.template)
mr.auth.user_id = 100
page_data = self.servlet.GatherPageData(mr)
self.assertNotIn('Restrict-View-Google', page_data['labels'])
pref = user_pb2.UserPrefValue(name='restrict_new_issues', value='true')
self.services.user.SetUserPrefs(self.cnxn, 100, [pref])
page_data = self.servlet.GatherPageData(mr)
self.assertIn('Restrict-View-Google', page_data['labels'])
def testGatherHelpData_Anon(self):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', project=self.project)
mr.auth.user_pb = user_pb2.User()
mr.auth.user_id = 0
help_data = self.servlet.GatherHelpData(mr, {})
self.assertEqual(
{'account_cue': None,
'cue': None,
'is_privileged_domain_user': None},
help_data)
def testGatherHelpData_NewUser(self):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', project=self.project)
mr.auth.user_pb = user_pb2.User(user_id=111)
mr.auth.user_id = 111
help_data = self.servlet.GatherHelpData(mr, {})
self.assertEqual(
{'account_cue': None,
'cue': 'privacy_click_through',
'is_privileged_domain_user': None},
help_data)
def testGatherHelpData_AlreadyClickedThroughPrivacy(self):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', project=self.project)
mr.auth.user_pb = user_pb2.User(user_id=111)
mr.auth.user_id = 111
self.services.user.SetUserPrefs(
self.cnxn, 111,
[user_pb2.UserPrefValue(name='privacy_click_through', value='true')])
help_data = self.servlet.GatherHelpData(mr, {})
self.assertEqual(
{'account_cue': None,
'cue': 'code_of_conduct',
'is_privileged_domain_user': None},
help_data)
def testGatherHelpData_DismissedEverything(self):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', project=self.project)
mr.auth.user_pb = user_pb2.User(user_id=111)
mr.auth.user_id = 111
self.services.user.SetUserPrefs(
self.cnxn, 111,
[user_pb2.UserPrefValue(name='privacy_click_through', value='true'),
user_pb2.UserPrefValue(name='code_of_conduct', value='true')])
help_data = self.servlet.GatherHelpData(mr, {})
self.assertEqual(
{'account_cue': None,
'cue': None,
'is_privileged_domain_user': None},
help_data)
@patch('framework.cloud_tasks_helpers.create_task')
def testProcessFormData_RedirectToEnteredIssue(self, _create_task_mock):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', project=self.project)
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
mr.template_name = 'rutabaga'
mr.auth.effective_ids = set([100])
post_data = fake.PostData(
template_name=['rutabaga'],
summary=['fake summary'],
comment=['fake comment'],
status=['New'])
self.mox.ReplayAll()
url = self.servlet.ProcessFormData(mr, post_data)
self.mox.VerifyAll()
self.assertTrue('/p/proj/issues/detail?id=' in url)
@patch('framework.cloud_tasks_helpers.create_task')
def testProcessFormData_AcceptWithFields(self, _create_task_mock):
"""We can create new issues with custom fields (restricted or not)."""
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', project=self.project)
mr.auth.user_view = framework_views.StuffUserView(100, 'admin@test', True)
mr.template_name = 'rutabaga'
mr.auth.effective_ids = set([100])
config = self.services.config.GetProjectConfig(
mr.cnxn, self.project.project_id)
config.field_defs = [
tracker_bizobj.MakeFieldDef(
1, 789, 'NonRestrictedField', tracker_pb2.FieldTypes.INT_TYPE, None,
'', False, False, False, None, None, '', False, '', '',
tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'NonRestrictedField',
False),
tracker_bizobj.MakeFieldDef(
2,
789,
'RestrictedField',
tracker_pb2.FieldTypes.INT_TYPE,
None,
'',
False,
False,
False,
None,
None,
'',
False,
'',
'',
tracker_pb2.NotifyTriggers.NEVER,
'no_action',
'RestrictedField',
False,
is_restricted_field=True),
tracker_bizobj.MakeFieldDef(
3,
789,
'RestrictedEnumField',
tracker_pb2.FieldTypes.ENUM_TYPE,
None,
'',
False,
False,
False,
None,
None,
'',
False,
'',
'',
tracker_pb2.NotifyTriggers.NEVER,
'no_action',
'RestrictedEnumField',
False,
is_restricted_field=True)
]
self.services.config.StoreConfig(mr.cnxn, config)
post_data = fake.PostData(
template_name=['rutabaga'],
summary=['fake summary'],
comment=['fake comment'],
custom_1=['3'],
custom_2=['7'],
label=['RestrictedEnumField-7'],
status=['New'])
self.mox.ReplayAll()
url = self.servlet.ProcessFormData(mr, post_data)
self.mox.VerifyAll()
self.assertTrue('/p/proj/issues/detail?id=' in url)
field_values = self.services.issue.issues_by_project[987][1].field_values
self.assertEqual(
self.services.issue.issues_by_project[987][1].labels,
['RestrictedEnumField-7'])
self.assertEqual(field_values[0].int_value, 3)
self.assertEqual(field_values[1].int_value, 7)
@patch('framework.cloud_tasks_helpers.create_task')
def testProcessFormData_AcceptEnforceTemplateRestrictedDefaultValues(
self, _create_task_mock):
"""The template applies default vals on fields that the user cannot edit."""
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', project=self.project)
mr.auth.user_view = framework_views.StuffUserView(100, 'admin@test', True)
mr.template_name = 'rutabaga'
mr.auth.effective_ids = set([100])
mr.perms = permissions.PermissionSet([])
config = self.services.config.GetProjectConfig(
mr.cnxn, self.project.project_id)
config.field_defs = [
tracker_bizobj.MakeFieldDef(
1, 789, 'NonRestrictedField', tracker_pb2.FieldTypes.INT_TYPE, None,
'', False, False, False, None, None, '', False, '', '',
tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'NonRestrictedField',
False),
tracker_bizobj.MakeFieldDef(
2,
789,
'RestrictedField',
tracker_pb2.FieldTypes.INT_TYPE,
None,
'',
False,
False,
False,
None,
None,
'',
False,
'',
'',
tracker_pb2.NotifyTriggers.NEVER,
'no_action',
'RestrictedField',
False,
is_restricted_field=True),
tracker_bizobj.MakeFieldDef(
3,
789,
'RestrictedEnumField',
tracker_pb2.FieldTypes.ENUM_TYPE,
None,
'',
False,
False,
False,
None,
None,
'',
False,
'',
'',
tracker_pb2.NotifyTriggers.NEVER,
'no_action',
'RestrictedEnumField',
False,
is_restricted_field=True)
]
self.services.config.StoreConfig(mr.cnxn, config)
post_data = fake.PostData(
template_name=['rutabaga'],
summary=['fake summary'],
comment=['fake comment'],
custom_1=['3'],
label=['Hey'],
status=['New'])
temp_restricted_fv = tracker_bizobj.MakeFieldValue(
2, 3737, None, None, None, None, False)
self.template.field_values.append(temp_restricted_fv)
self.template.labels.append('RestrictedEnumField-b')
self.mox.ReplayAll()
url = self.servlet.ProcessFormData(mr, post_data)
self.mox.VerifyAll()
self.assertTrue('/p/proj/issues/detail?id=' in url)
field_values = self.services.issue.issues_by_project[987][1].field_values
self.assertEqual(
self.services.issue.issues_by_project[987][1].labels,
['Hey', 'RestrictedEnumField-b'])
self.assertEqual(field_values[0].int_value, 3)
self.assertEqual(field_values[1].int_value, 3737)
def testProcessFormData_RejectNewLabels(self):
"""We raise an AssertionError when new labels are added."""
mr = testing_helpers.MakeMonorailRequest(path='/p/proj/issues/entry')
mr.perms = permissions.USER_PERMISSIONSET
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
post_data = fake.PostData(
template_name=['rutabaga'],
summary=['Nya nya I modified the summary'],
comment=[self.template.content],
status=['New'],
label=['freeze_new_label'])
self.mox.StubOutWithMock(self.servlet, 'PleaseCorrect')
self.servlet.PleaseCorrect(
mr,
component_required=None,
fields=[],
initial_blocked_on='',
initial_blocking='',
initial_cc='',
initial_comment=self.template.content,
initial_components='',
initial_owner='',
initial_status='New',
initial_summary='Nya nya I modified the summary',
initial_hotlists='',
labels=['freeze_new_label'],
template_name='rutabaga')
self.mox.ReplayAll()
url = self.servlet.ProcessFormData(mr, post_data)
self.mox.VerifyAll()
self.assertEqual(
(
"The creation of new labels is blocked for the Chromium project"
" in Monorail. To continue with editing your issue, please"
" remove: freeze_new_label label(s)."),
mr.errors.labels,
)
self.assertIsNone(url)
def testProcessFormData_RejectRestrictedFields(self):
"""We raise an AssertionError when restricted fields are set w/o perms."""
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', project=self.project)
mr.auth.user_view = framework_views.StuffUserView(
100, 'non-admin@test', True)
mr.template_name = 'rutabaga'
mr.auth.effective_ids = set([100])
mr.perms = permissions.PermissionSet([])
config = self.services.config.GetProjectConfig(
mr.cnxn, self.project.project_id)
config.field_defs = [
tracker_bizobj.MakeFieldDef(
1, 789, 'NonRestrictedField', tracker_pb2.FieldTypes.INT_TYPE, None,
'', False, False, False, None, None, '', False, '', '',
tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'NonRestrictedField',
False),
tracker_bizobj.MakeFieldDef(
2,
789,
'RestrictedField',
tracker_pb2.FieldTypes.INT_TYPE,
None,
'',
False,
False,
False,
None,
None,
'',
False,
'',
'',
tracker_pb2.NotifyTriggers.NEVER,
'no_action',
'RestrictedField',
False,
is_restricted_field=True),
tracker_bizobj.MakeFieldDef(
3,
789,
'RestrictedEnumField',
tracker_pb2.FieldTypes.ENUM_TYPE,
None,
'',
False,
False,
False,
None,
None,
'',
False,
'',
'',
tracker_pb2.NotifyTriggers.NEVER,
'no_action',
'RestrictedEnumField',
False,
is_restricted_field=True)
]
self.services.config.StoreConfig(mr.cnxn, config)
post_data_add_fv = fake.PostData(
template_name=['rutabaga'],
summary=['fake summary'],
comment=['fake comment'],
custom_1=['3'],
custom_2=['7'],
status=['New'])
post_data_label_edits_enum = fake.PostData(
template_name=['rutabaga'],
summary=['fake summary'],
comment=['fake comment'],
label=['RestrictedEnumField-7'],
status=['New'])
self.assertRaises(
AssertionError, self.servlet.ProcessFormData, mr, post_data_add_fv)
self.assertRaises(
AssertionError, self.servlet.ProcessFormData, mr,
post_data_label_edits_enum)
def testProcessFormData_RejectPlacedholderSummary(self):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry')
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
mr.perms = permissions.USER_PERMISSIONSET
mr.template_name = 'rutabaga'
post_data = fake.PostData(
template_name=['rutabaga'],
summary=[issueentry.PLACEHOLDER_SUMMARY],
comment=['fake comment'],
status=['New'])
self.mox.StubOutWithMock(self.servlet, 'PleaseCorrect')
self.servlet.PleaseCorrect(
mr, component_required=None, fields=[], initial_blocked_on='',
initial_blocking='', initial_cc='', initial_comment='fake comment',
initial_components='', initial_owner='', initial_status='New',
initial_summary='Enter one-line summary', initial_hotlists='',
labels=[], template_name='rutabaga')
self.mox.ReplayAll()
url = self.servlet.ProcessFormData(mr, post_data)
self.mox.VerifyAll()
self.assertEqual('Summary is required', mr.errors.summary)
self.assertIsNone(url)
def testProcessFormData_RejectUnmodifiedTemplate(self):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry')
mr.perms = permissions.USER_PERMISSIONSET
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
post_data = fake.PostData(
template_name=['rutabaga'],
summary=['Nya nya I modified the summary'],
comment=[self.template.content],
status=['New'])
self.mox.StubOutWithMock(self.servlet, 'PleaseCorrect')
self.servlet.PleaseCorrect(
mr, component_required=None, fields=[], initial_blocked_on='',
initial_blocking='', initial_cc='',
initial_comment=self.template.content, initial_components='',
initial_owner='', initial_status='New',
initial_summary='Nya nya I modified the summary', initial_hotlists='',
labels=[], template_name='rutabaga')
self.mox.ReplayAll()
url = self.servlet.ProcessFormData(mr, post_data)
self.mox.VerifyAll()
self.assertEqual('Template must be filled out.', mr.errors.comment)
self.assertIsNone(url)
def testProcessFormData_RejectNonexistentHotlist(self):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', user_info={'user_id': 111})
entered_hotlists = 'H3'
post_data = fake.PostData(hotlists=[entered_hotlists],
template_name=['rutabaga'])
self.mox.StubOutWithMock(self.servlet, 'PleaseCorrect')
self.servlet.PleaseCorrect(
mr, component_required=None, fields=[], initial_blocked_on='',
initial_blocking='', initial_cc='', initial_comment='',
initial_components='', initial_owner='', initial_status='',
initial_summary='', initial_hotlists=entered_hotlists, labels=[],
template_name='rutabaga')
self.mox.ReplayAll()
url = self.servlet.ProcessFormData(mr, post_data)
self.mox.VerifyAll()
self.assertEqual('You have no hotlist(s) named: H3', mr.errors.hotlists)
self.assertIsNone(url)
def testProcessFormData_RejectNonexistentHotlistOwner(self):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', user_info={'user_id': 111})
entered_hotlists = 'abc:H1'
post_data = fake.PostData(hotlists=[entered_hotlists],
template_name=['rutabaga'])
self.mox.StubOutWithMock(self.servlet, 'PleaseCorrect')
self.servlet.PleaseCorrect(
mr, component_required=None, fields=[], initial_blocked_on='',
initial_blocking='', initial_cc='', initial_comment='',
initial_components='', initial_owner='', initial_status='',
initial_summary='', initial_hotlists=entered_hotlists, labels=[],
template_name='rutabaga')
self.mox.ReplayAll()
url = self.servlet.ProcessFormData(mr, post_data)
self.mox.VerifyAll()
self.assertEqual('You have no hotlist(s) owned by: abc', mr.errors.hotlists)
self.assertIsNone(url)
def testProcessFormData_RejectInvalidHotlistName(self):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', user_info={'user_id': 111})
entered_hotlists = 'U1:H2'
post_data = fake.PostData(hotlists=[entered_hotlists],
template_name=['rutabaga'])
self.mox.StubOutWithMock(self.servlet, 'PleaseCorrect')
self.servlet.PleaseCorrect(
mr, component_required=None, fields=[], initial_blocked_on='',
initial_blocking='', initial_cc='', initial_comment='',
initial_components='', initial_owner='', initial_status='',
initial_summary='', initial_hotlists=entered_hotlists, labels=[],
template_name='rutabaga')
self.mox.ReplayAll()
url = self.servlet.ProcessFormData(mr, post_data)
self.mox.VerifyAll()
self.assertEqual('Not in your hotlist(s): U1:H2', mr.errors.hotlists)
self.assertIsNone(url)
def testProcessFormData_RejectDeprecatedComponent(self):
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry',
user_info={'user_id': 111},
project=self.project)
config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
config.component_defs = [
tracker_bizobj.MakeComponentDef(
1, mr.project_id, 'active', '', False, [], [], 0, 0),
tracker_bizobj.MakeComponentDef(
2, mr.project_id, 'notactive', '', True, [], [], 0, 0),
]
self.services.config.StoreConfig(mr.cnxn, config)
print(config)
post_data = fake.PostData(
template_name=['rutabaga'],
summary=['fake summary'],
comment=['fake comment'],
components=['notactive'])
self.mox.StubOutWithMock(self.servlet, 'PleaseCorrect')
self.servlet.PleaseCorrect(
mr,
component_required=None,
fields=[],
initial_blocked_on='',
initial_blocking='',
initial_cc='',
initial_comment='fake comment',
initial_components='notactive',
initial_owner='',
initial_status='',
initial_summary='fake summary',
initial_hotlists='',
labels=[],
template_name='rutabaga')
self.mox.ReplayAll()
url = self.servlet.ProcessFormData(mr, post_data)
self.mox.VerifyAll()
self.assertEqual(mr.errors.components, 'Undefined or deprecated component')
self.assertIsNone(url)
@patch('framework.cloud_tasks_helpers.create_task')
def testProcessFormData_TemplateNameMissing(self, _create_task_mock):
"""POST doesn't fail if no template_name is passed."""
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', project=self.project)
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
mr.auth.effective_ids = set([100])
self.services.template.GetTemplateById.return_value = None
self.services.template.GetProjectTemplates.return_value = [
tracker_pb2.TemplateDef(members_only=True, content=''),
tracker_pb2.TemplateDef(members_only=False, content='')]
post_data = fake.PostData(
summary=['fake summary'],
comment=['fake comment'],
status=['New'])
self.mox.ReplayAll()
url = self.servlet.ProcessFormData(mr, post_data)
self.mox.VerifyAll()
self.assertTrue('/p/proj/issues/detail?id=' in url)
@patch('framework.cloud_tasks_helpers.create_task')
def testProcessFormData_AcceptsFederatedReferences(self, _create_task_mock):
"""ProcessFormData accepts federated references."""
mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/issues/entry', project=self.project)
mr.auth.user_view = framework_views.StuffUserView(100, 'user@invalid', True)
mr.auth.effective_ids = set([100])
post_data = fake.PostData(
summary=['fake summary'],
comment=['fake comment'],
status=['New'],
template_name='rutabaga',
blocking=['b/123, b/987'],
blockedon=['b/456, b/654'])
self.mox.ReplayAll()
self.servlet.ProcessFormData(mr, post_data)
self.mox.VerifyAll()
self.assertIsNone(mr.errors.blockedon)
self.assertIsNone(mr.errors.blocking)
def testAttachDefaultApprovers(self):
config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
config.approval_defs = [
tracker_pb2.ApprovalDef(
approval_id=23, approver_ids=[222], survey='Question?'),
tracker_pb2.ApprovalDef(
approval_id=24, approver_ids=[111], survey='Question?')]
approval_values = [tracker_pb2.ApprovalValue(
approval_id=24, phase_id=1,
status=tracker_pb2.ApprovalStatus.NEEDS_REVIEW)]
issueentry._AttachDefaultApprovers(config, approval_values)
self.assertEqual(approval_values[0].approver_ids, [111])
# TODO(aneeshm): add a test for the ambiguous hotlist name case; it works
# correctly when tested locally, but for some reason doesn't in the test
# environment. Probably a result of some quirk in fake.py?