Merge branch 'main' into avm99963-monorail

Merged commit 34d8229ae2b51fb1a15bd208e6fe6185c94f6266

GitOrigin-RevId: 7ee0917f93a577e475f8e09526dd144d245593f4
diff --git a/services/test/api_svc_v1_test.py b/services/test/api_svc_v1_test.py
index b7cd9b1..72f7aee 100644
--- a/services/test/api_svc_v1_test.py
+++ b/services/test/api_svc_v1_test.py
@@ -1,7 +1,6 @@
-# Copyright 2016 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 or at
-# https://developers.google.com/open-source/licenses/bsd
+# 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 API v1."""
 from __future__ import print_function
@@ -9,9 +8,11 @@
 from __future__ import absolute_import
 
 import datetime
+from unittest import mock
 import endpoints
 import logging
 from mock import Mock, patch, ANY
+import six
 import time
 import unittest
 import webtest
@@ -27,9 +28,9 @@
 from framework import permissions
 from framework import profiler
 from framework import template_helpers
-from proto import api_pb2_v1
-from proto import project_pb2
-from proto import tracker_pb2
+from mrproto import api_pb2_v1
+from mrproto import project_pb2
+from mrproto import tracker_pb2
 from search import frontendsearchpipeline
 from services import api_svc_v1
 from services import service_manager
@@ -40,6 +41,7 @@
 from testing_utils import testing
 from tracker import tracker_bizobj
 from tracker import tracker_constants
+from redirect import redirect_utils
 
 
 def MakeFakeServiceManager():
@@ -163,7 +165,7 @@
     oauth.get_current_user.side_effect = oauth.Error()
     with self.assertRaises(webtest.AppError) as cm:
       self.call_api('users_get', self.request)
-    self.assertTrue(cm.exception.message.startswith('Bad response: 401'))
+    self.assertTrue(str(cm.exception).startswith('Bad response: 401'))
 
 
 class MonorailApiTest(testing.EndpointsTestCase):
@@ -198,6 +200,7 @@
               lambda x, y, z, u, v, w: ('id', 'email'))
 
     self.mock(tracker_fulltext, 'IndexIssues', lambda x, y, z, u, v: None)
+    self.mock(tracker_fulltext, 'UnindexIssues', lambda _: None)
 
   def SetUpComponents(
       self, project_id, component_id, component_name, component_doc='doc',
@@ -303,6 +306,85 @@
     self.assertEqual('Field1', resp['fieldValues'][0]['fieldName'])
     self.assertEqual('11', resp['fieldValues'][0]['fieldValue'])
 
+  @mock.patch('businesslogic.work_env.WorkEnv.GetIssueMigratedID')
+  def testIssuesGet_GetIssue_MigratedId(self, mockGetIssueMigratedId):
+    """Get the requested issue."""
+    mockGetIssueMigratedId.return_value = '23456'
+
+    self.services.project.TestAddProject(
+        'test-project', owner_ids=[222], project_id=12345)
+    self.SetUpComponents(12345, 1, 'API')
+    self.SetUpFieldDefs(1, 12345, 'Field1', tracker_pb2.FieldTypes.INT_TYPE)
+
+    fv = tracker_pb2.FieldValue(field_id=1, int_value=11)
+    issue1 = fake.MakeTestIssue(
+        project_id=12345,
+        local_id=1,
+        owner_id=222,
+        reporter_id=111,
+        status='New',
+        summary='sum',
+        component_ids=[1],
+        field_values=[fv])
+    self.services.issue.TestAddIssue(issue1)
+
+    resp = self.call_api('issues_get', self.request).json_body
+    self.assertEqual(1, resp['id'])
+    self.assertEqual('New', resp['status'])
+    self.assertEqual('open', resp['state'])
+    self.assertFalse(resp['canEdit'])
+    self.assertTrue(resp['canComment'])
+    self.assertEqual('requester@example.com', resp['author']['name'])
+    self.assertEqual('user@example.com', resp['owner']['name'])
+    self.assertEqual('API', resp['components'][0])
+    self.assertEqual('Field1', resp['fieldValues'][0]['fieldName'])
+    self.assertEqual('11', resp['fieldValues'][0]['fieldValue'])
+    self.assertEqual('23456', resp['migrated_id'])
+
+  @patch('framework.cloud_tasks_helpers.create_task')
+  def testIssuesInsert_FreezeLabels(self, _create_task_mock):
+    """Attempts to add new labels are blocked"""
+    self.services.project.TestAddProject(
+        'test-project', owner_ids=[222], committer_ids=[111], project_id=999)
+    self.SetUpFieldDefs(1, 999, 'Field1', tracker_pb2.FieldTypes.INT_TYPE)
+
+    issue1 = fake.MakeTestIssue(
+        project_id=999,
+        local_id=1,
+        owner_id=222,
+        reporter_id=111,
+        status='New',
+        summary='Test issue')
+    self.services.issue.TestAddIssue(issue1)
+
+    issue_dict = {
+        'blockedOn': [{
+            'issueId': 1
+        }],
+        'cc': [{
+            'name': 'user@example.com'
+        }, {
+            'name': ''
+        }, {
+            'name': ' '
+        }],
+        'description': 'description',
+        'labels': ['freeze_new_label', 'label1'],
+        'owner': {
+            'name': 'requester@example.com'
+        },
+        'status': 'New',
+        'summary': 'Test issue',
+        'fieldValues': [{
+            'fieldName': 'Field1',
+            'fieldValue': '11'
+        }]
+    }
+    self.request.update(issue_dict)
+
+    with self.call_should_fail(400):
+      self.call_api('issues_insert', self.request)
+
   def testIssuesInsert_BadRequest(self):
     """The request does not specify summary or status."""
 
@@ -573,6 +655,36 @@
     with self.call_should_fail(403):
       self.call_api('issues_comments_insert', self.request)
 
+  def testIssuesCommentsInsert_ArchivedProject(self):
+    """No permission to comment in an archived project."""
+    self.services.project.TestAddProject(
+        'test-project',
+        owner_ids=[111],
+        state=project_pb2.ProjectState.ARCHIVED,
+        project_id=12345)
+    issue1 = fake.MakeTestIssue(12345, 1, 'Issue 1', 'New', 2)
+    self.services.issue.TestAddIssue(issue1)
+
+    self.services.project.TestAddProject(
+        'archived-project', owner_ids=[222], project_id=6789)
+    issue2 = fake.MakeTestIssue(
+        6789, 2, 'Issue 2', 'New', 222, project_name='archived-project')
+    self.services.issue.TestAddIssue(issue2)
+
+    self.request['updates'] = {
+        'blockedOn': ['archived-project:2'],
+        'mergedInto': '',
+    }
+    with self.call_should_fail(403):
+      self.call_api('issues_comments_insert', self.request)
+
+    self.request['updates'] = {
+        'blockedOn': [],
+        'mergedInto': 'archived-project:2',
+    }
+    with self.call_should_fail(403):
+      self.call_api('issues_comments_insert', self.request)
+
   def testIssuesCommentsInsert_CommentPermissionOnly(self):
     """User has permission to comment, even though they cannot edit."""
     self.services.project.TestAddProject(
@@ -600,6 +712,28 @@
     with self.call_should_fail(400):
       self.call_api('issues_comments_insert', self.request)
 
+  def testIssuesCommentsInsert_FreezeLabels(self):
+    """Attempts to add new labels are blocked"""
+    self.services.project.TestAddProject(
+        'test-project', owner_ids=[111], project_id=999)
+
+    issue1 = fake.MakeTestIssue(
+        999, 1, 'Issue 1', 'New', 222, project_name='test-project')
+    self.services.issue.TestAddIssue(issue1)
+
+    self.request['updates'] = {
+        'summary': 'new summary',
+        'status': 'Started',
+        'owner': 'requester@example.com',
+        'cc': ['user@example.com'],
+        'labels': ['freeze_new_label', '-remove_label'],
+        'blockedOn': ['2'],
+        'blocking': ['3'],
+    }
+
+    with self.call_should_fail(400):
+      self.call_api('issues_comments_insert', self.request)
+
   def testIssuesCommentsInsert_Amendments_Normal(self):
     """Insert comments with amendments."""
 
@@ -703,10 +837,10 @@
     self.assertEqual(2, len(issue2_comments))  # description and merge
     source_starrers = self.services.issue_star.LookupItemStarrers(
         'cnxn', issue1.issue_id)
-    self.assertItemsEqual([111, 222, 333], source_starrers)
+    six.assertCountEqual(self, [111, 222, 333], source_starrers)
     target_starrers = self.services.issue_star.LookupItemStarrers(
         'cnxn', issue2.issue_id)
-    self.assertItemsEqual([111, 222, 333, 555], target_starrers)
+    six.assertCountEqual(self, [111, 222, 333, 555], target_starrers)
 
   def testIssuesCommentsInsert_CustomFields(self):
     """Update custom field values."""
@@ -1470,9 +1604,13 @@
     with self.call_should_fail(403):
       self.call_api('groups_create', self.request)
 
-  def SetUpGroupRequest(self, group_name, who_can_view_members='MEMBERS',
-                        ext_group_type=None, perms=None,
-                        requester='requester@example.com'):
+  def SetUpGroupRequest(
+      self,
+      group_name,
+      who_can_view_members='MEMBERS',
+      ext_group_type='CHROME_INFRA_AUTH',
+      perms=None,
+      requester='requester@example.com'):
     request = {
         'groupName': group_name,
         'requester': requester,
@@ -1648,7 +1786,8 @@
     cd_dict = {
       'componentPath': 'API'}
     self.request.update(cd_dict)
-    _ = self.call_api('components_delete', self.request).json_body
+    with self.assertWarns(webtest.lint.WSGIWarning):
+      _ = self.call_api('components_delete', self.request)
     self.assertEqual(0, len(self.config.component_defs))
 
   def testComponentsUpdate_Invalid(self):
@@ -1704,12 +1843,13 @@
               'requester@example.com', 'user@example.com', '', ' ']},
           {'field': 'DEPRECATED', 'deprecated': True}]}
     self.request.update(cd_dict)
-    _ = self.call_api('components_update', self.request).json_body
+    with self.assertWarns(webtest.lint.WSGIWarning):
+      _ = self.call_api('components_update', self.request)
     component_def = tracker_bizobj.FindComponentDef(
         'API', self.config)
     self.assertIsNotNone(component_def)
     self.assertEqual('', component_def.docstring)
-    self.assertItemsEqual([111, 222], component_def.cc_ids)
+    six.assertCountEqual(self, [111, 222], component_def.cc_ids)
     self.assertTrue(component_def.deprecated)
 
     cd_dict = {
@@ -1717,7 +1857,8 @@
       'updates': [
           {'field': 'LEAF_NAME', 'leafName': 'NewParent'}]}
     self.request.update(cd_dict)
-    _ = self.call_api('components_update', self.request).json_body
+    with self.assertWarns(webtest.lint.WSGIWarning):
+      _ = self.call_api('components_update', self.request)
     cd_parent = tracker_bizobj.FindComponentDef(
         'NewParent', self.config)
     cd_child = tracker_bizobj.FindComponentDef(
@@ -1838,6 +1979,21 @@
       api_svc_v1.api_base_checks(
           request, requester, self.services, None, self.auth_client_ids, [])
 
+  def testNonLiveMigratedProject(self):
+    archived_project = 'archived-migrated-project'
+    redirect_utils.PROJECT_REDIRECT_MAP = {
+        'archived-migrated-project': 'https://example.dev'
+    }
+    self.services.project.TestAddProject(
+        archived_project,
+        owner_ids=[111],
+        state=project_pb2.ProjectState.ARCHIVED)
+    request = RequestMock()
+    request.projectId = archived_project
+    requester = RequesterMock(email='test@example.com')
+    api_svc_v1.api_base_checks(
+        request, requester, self.services, None, self.auth_client_ids, [])
+
   def testNoViewProjectPermission(self):
     nonmember_email = 'nonmember@example.com'
     self.services.user.TestAddUser(nonmember_email, 222)