blob: c19a21d34922a70d48f26e0b6a1858171ad2f1da [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.
"""Tests for the issue admin pages."""
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
try:
from mox3 import mox
except ImportError:
import mox
import six
import unittest
import settings
from mock import Mock, patch
from framework import permissions
from framework import urls
from mrproto import tracker_pb2
from services import service_manager
from services import template_svc
from testing import fake
from testing import testing_helpers
from tracker import issueadmin
from tracker import tracker_bizobj
from tracker import tracker_constants
class TestBase(unittest.TestCase):
def setUpServlet(self, servlet_factory):
# pylint: disable=attribute-defined-outside-init
self.services = service_manager.Services(
project=fake.ProjectService(),
config=fake.ConfigService(),
user=fake.UserService(),
issue=fake.IssueService(),
template=Mock(spec=template_svc.TemplateService),
features=fake.FeaturesService())
self.servlet = servlet_factory(services=self.services)
self.project = self.services.project.TestAddProject(
'proj', project_id=789, contrib_ids=[333])
self.config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
self.services.config.StoreConfig(None, self.config)
self.cnxn = fake.MonorailConnection()
self.mr = testing_helpers.MakeMonorailRequest(
path='/p/proj/admin', project=self.project)
# Default to admin perms given that most tests assume the user can edit.
self.mr.perms = permissions.ADMIN_PERMISSIONSET
self.mox = mox.Mox()
self.test_template = tracker_bizobj.MakeIssueTemplate(
'Test Template', 'sum', 'New', 111, 'content', [], [], [], [])
self.test_template.template_id = 12345
self.test_templates = testing_helpers.DefaultTemplates()
self.test_templates.append(self.test_template)
self.services.template.GetProjectTemplates\
.return_value = self.test_templates
self.services.template.GetTemplateSetForProject\
.return_value = [(12345, 'Test template', 0)]
settings.config_freeze_project_ids = {}
def tearDown(self):
self.mox.UnsetStubs()
self.mox.ResetAll()
def _mockGetUser(self):
self.mox.StubOutWithMock(self.services.user, 'GetUser')
user = self.services.user.TestAddUser('user@invalid', 100)
self.services.user.GetUser(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(user)
class IssueAdminBaseTest(TestBase):
def setUp(self):
super(IssueAdminBaseTest, self).setUpServlet(issueadmin.IssueAdminBase)
def testGatherPageData(self):
self._mockGetUser()
self.mox.ReplayAll()
page_data = self.servlet.GatherPageData(self.mr)
self.mox.VerifyAll()
six.assertCountEqual(
self, [
'admin_tab_mode', 'config', 'open_text', 'closed_text',
'labels_text', 'can_edit_project'
], list(page_data.keys()))
config_view = page_data['config']
self.assertEqual(789, config_view.project_id)
class AdminStatusesTest(TestBase):
def setUp(self):
super(AdminStatusesTest, self).setUpServlet(issueadmin.AdminStatuses)
self.servlet.mr = self.mr
@patch('framework.servlet.Servlet.PleaseCorrect')
def testProcessSubtabForm_MissingInput(self, mock_pc):
post_data = fake.PostData()
next_url = self.servlet.ProcessSubtabForm(post_data, self.mr)
self.assertIsNone(next_url)
mock_pc.assert_called_once()
self.assertEqual(len(tracker_constants.DEFAULT_WELL_KNOWN_STATUSES),
len(self.config.well_known_statuses))
self.assertEqual(tracker_constants.DEFAULT_STATUSES_OFFER_MERGE,
self.config.statuses_offer_merge)
@patch('framework.servlet.Servlet.PleaseCorrect')
def testProcessSubtabForm_EmptyInput(self, mock_pc):
post_data = fake.PostData(
predefinedopen=[''], predefinedclosed=[''], statuses_offer_merge=[''])
next_url = self.servlet.ProcessSubtabForm(post_data, self.mr)
self.assertIsNone(next_url)
mock_pc.assert_called_once()
self.assertEqual(len(tracker_constants.DEFAULT_WELL_KNOWN_STATUSES),
len(self.config.well_known_statuses))
self.assertEqual(tracker_constants.DEFAULT_STATUSES_OFFER_MERGE,
self.config.statuses_offer_merge)
def testProcessSubtabForm_Normal(self):
post_data = fake.PostData(
predefinedopen=['New = newly reported'],
predefinedclosed=['Fixed\nDuplicate'],
statuses_offer_merge=['Duplicate'])
next_url = self.servlet.ProcessSubtabForm(post_data, self.mr)
self.assertEqual(urls.ADMIN_STATUSES, next_url)
self.assertEqual(3, len(self.config.well_known_statuses))
self.assertEqual('New', self.config.well_known_statuses[0].status)
self.assertTrue(self.config.well_known_statuses[0].means_open)
self.assertEqual('Fixed', self.config.well_known_statuses[1].status)
self.assertFalse(self.config.well_known_statuses[1].means_open)
self.assertEqual('Duplicate', self.config.well_known_statuses[2].status)
self.assertFalse(self.config.well_known_statuses[2].means_open)
self.assertEqual(['Duplicate'], self.config.statuses_offer_merge)
class AdminLabelsTest(TestBase):
def setUp(self):
super(AdminLabelsTest, self).setUpServlet(issueadmin.AdminLabels)
def testGatherPageData(self):
self._mockGetUser()
self.mox.ReplayAll()
page_data = self.servlet.GatherPageData(self.mr)
self.mox.VerifyAll()
six.assertCountEqual(
self, [
'admin_tab_mode', 'config', 'field_defs', 'open_text',
'closed_text', 'labels_text', 'can_edit_project'
], list(page_data.keys()))
config_view = page_data['config']
self.assertEqual(789, config_view.project_id)
self.assertEqual([], page_data['field_defs'])
@patch('framework.servlet.Servlet.PleaseCorrect')
def testProcessSubtabForm_MissingInput(self, mock_pc):
post_data = fake.PostData()
next_url = self.servlet.ProcessSubtabForm(post_data, self.mr)
self.assertIsNone(next_url)
mock_pc.assert_called_once()
self.assertEqual(len(tracker_constants.DEFAULT_WELL_KNOWN_LABELS),
len(self.config.well_known_labels))
self.assertEqual(tracker_constants.DEFAULT_EXCL_LABEL_PREFIXES,
self.config.exclusive_label_prefixes)
@patch('framework.servlet.Servlet.PleaseCorrect')
def testProcessSubtabForm_EmptyInput(self, mock_pc):
post_data = fake.PostData(
predefinedlabels=[''], excl_prefixes=[''])
next_url = self.servlet.ProcessSubtabForm(post_data, self.mr)
self.assertIsNone(next_url) # Because PleaseCorrect() was called.
mock_pc.assert_called_once()
self.assertEqual(len(tracker_constants.DEFAULT_WELL_KNOWN_LABELS),
len(self.config.well_known_labels))
self.assertEqual(tracker_constants.DEFAULT_EXCL_LABEL_PREFIXES,
self.config.exclusive_label_prefixes)
def testProcessSubtabForm_Normal(self):
post_data = fake.PostData(
predefinedlabels=['Pri-0 = Burning issue\nPri-4 = It can wait'],
excl_prefixes=['pri'])
next_url = self.servlet.ProcessSubtabForm(post_data, self.mr)
self.assertEqual(urls.ADMIN_LABELS, next_url)
self.assertEqual(2, len(self.config.well_known_labels))
self.assertEqual('Pri-0', self.config.well_known_labels[0].label)
self.assertEqual('Pri-4', self.config.well_known_labels[1].label)
self.assertEqual(['pri'], self.config.exclusive_label_prefixes)
@patch('framework.servlet.Servlet.PleaseCorrect')
def testProcessSubtabForm_Duplicates(self, mock_pc):
post_data = fake.PostData(
predefinedlabels=['Pri-0\nPri-4\npri-0'],
excl_prefixes=['pri'])
next_url = self.servlet.ProcessSubtabForm(post_data, self.mr)
self.assertIsNone(next_url)
mock_pc.assert_called_once()
self.assertEqual(
'Duplicate label: pri-0',
self.mr.errors.label_defs)
@patch('framework.servlet.Servlet.PleaseCorrect')
def testProcessSubtabForm_Conflict(self, mock_pc):
post_data = fake.PostData(
predefinedlabels=['Multi-Part-One\nPri-4\npri-0'],
excl_prefixes=['pri'])
self.config.field_defs = [
tracker_pb2.FieldDef(
field_name='Multi-Part',
field_type=tracker_pb2.FieldTypes.ENUM_TYPE)]
next_url = self.servlet.ProcessSubtabForm(post_data, self.mr)
self.assertIsNone(next_url)
mock_pc.assert_called_once()
self.assertEqual(
'Label "Multi-Part-One" should be defined in enum "multi-part"',
self.mr.errors.label_defs)
class AdminTemplatesTest(TestBase):
def setUp(self):
super(AdminTemplatesTest, self).setUpServlet(issueadmin.AdminTemplates)
self.mr.auth.user_id = 333
self.mr.auth.effective_ids = {333}
def testGatherPageData(self):
self._mockGetUser()
self.mox.ReplayAll()
page_data = self.servlet.GatherPageData(self.mr)
self.mox.VerifyAll()
config_view = page_data['config']
self.assertEqual(789, config_view.project_id)
def testProcessSubtabForm_NoEditProjectPerm(self):
"""If user lacks perms, raise an exception."""
post_data = fake.PostData(
default_template_for_developers=['Test Template'],
default_template_for_users=['Test Template'])
self.mr.perms = permissions.EMPTY_PERMISSIONSET
self.assertRaises(
permissions.PermissionException,
self.servlet.ProcessSubtabForm, post_data, self.mr)
self.assertEqual(0, self.config.default_template_for_developers)
self.assertEqual(0, self.config.default_template_for_users)
def testProcessSubtabForm_Normal(self):
"""If user has perms, set default templates."""
post_data = fake.PostData(
default_template_for_developers=['Test Template'],
default_template_for_users=['Test Template'])
next_url = self.servlet.ProcessSubtabForm(post_data, self.mr)
self.assertEqual(urls.ADMIN_TEMPLATES, next_url)
self.assertEqual(12345, self.config.default_template_for_developers)
self.assertEqual(12345, self.config.default_template_for_users)
def testParseDefaultTemplateSelections_NotSpecified(self):
post_data = fake.PostData()
for_devs, for_users = self.servlet._ParseDefaultTemplateSelections(
post_data, self.test_templates)
self.assertEqual(None, for_devs)
self.assertEqual(None, for_users)
def testParseDefaultTemplateSelections_TemplateNotFoundIsIgnored(self):
post_data = fake.PostData(
default_template_for_developers=['Bad value'],
default_template_for_users=['Bad value'])
for_devs, for_users = self.servlet._ParseDefaultTemplateSelections(
post_data, self.test_templates)
self.assertEqual(None, for_devs)
self.assertEqual(None, for_users)
def testParseDefaultTemplateSelections_Normal(self):
post_data = fake.PostData(
default_template_for_developers=['Test Template'],
default_template_for_users=['Test Template'])
for_devs, for_users = self.servlet._ParseDefaultTemplateSelections(
post_data, self.test_templates)
self.assertEqual(12345, for_devs)
self.assertEqual(12345, for_users)
class AdminComponentsTest(TestBase):
def setUp(self):
super(AdminComponentsTest, self).setUpServlet(issueadmin.AdminComponents)
self.cd_clean = tracker_bizobj.MakeComponentDef(
1, self.project.project_id, 'BackEnd', 'doc', False, [], [111], 100000,
122, 10000000, 133)
self.cd_with_subcomp = tracker_bizobj.MakeComponentDef(
2, self.project.project_id, 'FrontEnd', 'doc', False, [], [111],
100000, 122, 10000000, 133)
self.subcd = tracker_bizobj.MakeComponentDef(
3, self.project.project_id, 'FrontEnd>Worker', 'doc', False, [], [111],
100000, 122, 10000000, 133)
self.cd_with_template = tracker_bizobj.MakeComponentDef(
4, self.project.project_id, 'Middle', 'doc', False, [], [111],
100000, 122, 10000000, 133)
def testGatherPageData(self):
self._mockGetUser()
self.mox.ReplayAll()
page_data = self.servlet.GatherPageData(self.mr)
self.mox.VerifyAll()
six.assertCountEqual(
self, [
'admin_tab_mode', 'failed_templ', 'component_defs', 'failed_perm',
'config', 'failed_subcomp', 'open_text', 'closed_text',
'labels_text', 'can_edit_project'
], list(page_data.keys()))
config_view = page_data['config']
self.assertEqual(789, config_view.project_id)
self.assertEqual([], page_data['component_defs'])
def testProcessFormData_NoErrors(self):
self.config.component_defs = [
self.cd_clean, self.cd_with_subcomp, self.subcd, self.cd_with_template]
self.services.template.TemplatesWithComponent.return_value = []
post_data = {
'delete_components' : '%s,%s,%s' % (
self.cd_clean.path, self.cd_with_subcomp.path, self.subcd.path)}
url = self.servlet.ProcessFormData(self.mr, post_data)
self.assertTrue(
url.startswith('http://127.0.0.1/p/proj/adminComponents?deleted='
'FrontEnd%3EWorker%2CFrontEnd%2CBackEnd&failed_perm=&'
'failed_subcomp=&failed_templ=&ts='))
def testProcessFormData_SubCompError(self):
self.config.component_defs = [
self.cd_clean, self.cd_with_subcomp, self.subcd, self.cd_with_template]
self.services.template.TemplatesWithComponent.return_value = []
post_data = {
'delete_components' : '%s,%s' % (
self.cd_clean.path, self.cd_with_subcomp.path)}
url = self.servlet.ProcessFormData(self.mr, post_data)
self.assertTrue(
url.startswith('http://127.0.0.1/p/proj/adminComponents?deleted='
'BackEnd&failed_perm=&failed_subcomp=FrontEnd&'
'failed_templ=&ts='))
def testProcessFormData_TemplateError(self):
self.config.component_defs = [
self.cd_clean, self.cd_with_subcomp, self.subcd, self.cd_with_template]
def mockTemplatesWithComponent(_cnxn, component_id):
if component_id == 4:
return 'template'
self.services.template.TemplatesWithComponent\
.side_effect = mockTemplatesWithComponent
post_data = {
'delete_components' : '%s,%s,%s,%s' % (
self.cd_clean.path, self.cd_with_subcomp.path, self.subcd.path,
self.cd_with_template.path)}
url = self.servlet.ProcessFormData(self.mr, post_data)
self.assertTrue(
url.startswith('http://127.0.0.1/p/proj/adminComponents?deleted='
'FrontEnd%3EWorker%2CFrontEnd%2CBackEnd&failed_perm=&'
'failed_subcomp=&failed_templ=Middle&ts='))
class AdminViewsTest(TestBase):
def setUp(self):
super(AdminViewsTest, self).setUpServlet(issueadmin.AdminViews)
def testGatherPageData(self):
self._mockGetUser()
self.mox.ReplayAll()
page_data = self.servlet.GatherPageData(self.mr)
self.mox.VerifyAll()
six.assertCountEqual(
self, [
'canned_queries', 'admin_tab_mode', 'config', 'issue_notify',
'new_query_indexes', 'max_queries', 'open_text', 'closed_text',
'labels_text', 'can_edit_project'
], list(page_data.keys()))
config_view = page_data['config']
self.assertEqual(789, config_view.project_id)
def testProcessSubtabForm(self):
post_data = fake.PostData(
default_col_spec=['id pri mstone owner status summary'],
default_sort_spec=['mstone pri'],
default_x_attr=['owner'], default_y_attr=['mstone'])
next_url = self.servlet.ProcessSubtabForm(post_data, self.mr)
self.assertEqual(urls.ADMIN_VIEWS, next_url)
self.assertEqual(
'id pri mstone owner status summary', self.config.default_col_spec)
self.assertEqual('mstone pri', self.config.default_sort_spec)
self.assertEqual('owner', self.config.default_x_attr)
self.assertEqual('mstone', self.config.default_y_attr)
class AdminViewsFunctionsTest(unittest.TestCase):
def testParseListPreferences(self):
# If no input, col_spec will be default column spec.
# For other fiels empty strings should be returned.
(col_spec, sort_spec, x_attr, y_attr, member_default_query,
) = issueadmin._ParseListPreferences({})
self.assertEqual(tracker_constants.DEFAULT_COL_SPEC, col_spec)
self.assertEqual('', sort_spec)
self.assertEqual('', x_attr)
self.assertEqual('', y_attr)
self.assertEqual('', member_default_query)
# Test how hyphens in input are treated.
spec = 'label1-sub1 label2 label3-sub3'
(col_spec, sort_spec, x_attr, y_attr, member_default_query,
) = issueadmin._ParseListPreferences(
fake.PostData(default_col_spec=[spec],
default_sort_spec=[spec],
default_x_attr=[spec],
default_y_attr=[spec]),
)
# Hyphens (and anything following) should be stripped from each term.
self.assertEqual('label1-sub1 label2 label3-sub3', col_spec)
# The sort spec should be as given (except with whitespace condensed).
self.assertEqual(' '.join(spec.split()), sort_spec)
# Only the first term (up to the first hyphen) should be used for x- or
# y-attr.
self.assertEqual('label1-sub1', x_attr)
self.assertEqual('label1-sub1', y_attr)
# Test that multibyte strings are not mangled.
spec = (
b'\xe7\xaa\xbf\xe8\x8b\xa5-\xe7\xb9\xb9 '
b'\xe5\x9c\xb0\xe3\x81\xa6-\xe5\xbd\x93-\xe3\x81\xbe\xe3\x81\x99')
spec = spec.decode('utf-8')
(col_spec, sort_spec, x_attr, y_attr, member_default_query,
) = issueadmin._ParseListPreferences(
fake.PostData(default_col_spec=[spec],
default_sort_spec=[spec],
default_x_attr=[spec],
default_y_attr=[spec],
member_default_query=[spec]),
)
self.assertEqual(spec, col_spec)
self.assertEqual(' '.join(spec.split()), sort_spec)
self.assertEqual(
b'\xe7\xaa\xbf\xe8\x8b\xa5-\xe7\xb9\xb9'.decode('utf-8'), x_attr)
self.assertEqual(
b'\xe7\xaa\xbf\xe8\x8b\xa5-\xe7\xb9\xb9'.decode('utf-8'), y_attr)
self.assertEqual(spec, member_default_query)
class AdminRulesTest(TestBase):
def setUp(self):
super(AdminRulesTest, self).setUpServlet(issueadmin.AdminRules)
def testGatherPageData(self):
self._mockGetUser()
self.mox.ReplayAll()
page_data = self.servlet.GatherPageData(self.mr)
self.mox.VerifyAll()
six.assertCountEqual(
self, [
'admin_tab_mode', 'config', 'rules', 'new_rule_indexes',
'max_rules', 'open_text', 'closed_text', 'labels_text',
'can_edit_project'
], list(page_data.keys()))
config_view = page_data['config']
self.assertEqual(789, config_view.project_id)
self.assertEqual([], page_data['rules'])
def testProcessSubtabForm(self):
pass # TODO(jrobbins): write this test