Merge branch 'main' into avm99963-monorail

Merged commit cd4b3b336f1f14afa02990fdc2eec5d9467a827e

GitOrigin-RevId: e67bbf185d5538e1472bb42e0abb2a141f88bac1
diff --git a/features/test/banspammer_test.py b/features/test/banspammer_test.py
index e6fceff..edf7aba 100644
--- a/features/test/banspammer_test.py
+++ b/features/test/banspammer_test.py
@@ -12,7 +12,7 @@
 import mock
 import os
 import unittest
-import urllib
+from six.moves import urllib
 import webapp2
 
 import settings
@@ -74,7 +74,7 @@
         'app_engine_http_request':
             {
                 'relative_uri': urls.BAN_SPAMMER_TASK + '.do',
-                'body': urllib.urlencode(params),
+                'body': urllib.parse.urlencode(params),
                 'headers': {
                     'Content-type': 'application/x-www-form-urlencoded'
                 }
diff --git a/features/test/component_helpers_test.py b/features/test/component_helpers_test.py
deleted file mode 100644
index aa6c761..0000000
--- a/features/test/component_helpers_test.py
+++ /dev/null
@@ -1,145 +0,0 @@
-# Copyright 2018 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
-
-"""Unit tests for component prediction endpoints."""
-from __future__ import print_function
-from __future__ import division
-from __future__ import absolute_import
-
-import json
-import mock
-import sys
-import unittest
-
-from services import service_manager
-from testing import fake
-
-# Mock cloudstorage before it's imported by component_helpers
-sys.modules['cloudstorage'] = mock.Mock()
-from features import component_helpers
-
-
-class FakeMLEngine(object):
-  def __init__(self, test):
-    self.test = test
-    self.expected_features = None
-    self.scores = None
-    self._execute_response = None
-
-  def projects(self):
-    return self
-
-  def models(self):
-    return self
-
-  def predict(self, name, body):
-    self.test.assertEqual(component_helpers.MODEL_NAME, name)
-    self.test.assertEqual(
-        {'instances': [{'inputs': self.expected_features}]}, body)
-    self._execute_response = {'predictions': [{'scores': self.scores}]}
-    return self
-
-  def get(self, name):
-    self.test.assertEqual(component_helpers.MODEL_NAME, name)
-    self._execute_response = {'defaultVersion': {'name': 'v_1234'}}
-    return self
-
-  def execute(self):
-    response = self._execute_response
-    self._execute_response = None
-    return response
-
-
-class ComponentHelpersTest(unittest.TestCase):
-
-  def setUp(self):
-    self.services = service_manager.Services(
-        config=fake.ConfigService(),
-        user=fake.UserService())
-    self.project = fake.Project(project_name='proj')
-
-    self._ml_engine = FakeMLEngine(self)
-    self._top_words = None
-    self._components_by_index = None
-
-    mock.patch(
-        'services.ml_helpers.setup_ml_engine', lambda: self._ml_engine).start()
-    mock.patch(
-        'features.component_helpers._GetTopWords',
-        lambda _: self._top_words).start()
-    mock.patch('cloudstorage.open', self.cloudstorageOpen).start()
-    mock.patch('settings.component_features', 5).start()
-
-    self.addCleanup(mock.patch.stopall)
-
-  def cloudstorageOpen(self, name, mode):
-    """Create a file mock that returns self._components_by_index when read."""
-    open_fn = mock.mock_open(read_data=json.dumps(self._components_by_index))
-    return open_fn(name, mode)
-
-  def testPredict_Normal(self):
-    """Test normal case when predicted component exists."""
-    component_id = self.services.config.CreateComponentDef(
-        cnxn=None, project_id=self.project.project_id, path='Ruta>Baga',
-        docstring='', deprecated=False, admin_ids=[], cc_ids=[], created=None,
-        creator_id=None, label_ids=[])
-    config = self.services.config.GetProjectConfig(
-        None, self.project.project_id)
-
-    self._top_words = {
-        'foo': 0,
-        'bar': 1,
-        'baz': 2}
-    self._components_by_index = {
-        '0': '123',
-        '1': str(component_id),
-        '2': '789'}
-    self._ml_engine.expected_features = [3, 0, 1, 0, 0]
-    self._ml_engine.scores = [5, 10, 3]
-
-    text = 'foo baz foo foo'
-
-    self.assertEqual(
-        component_id, component_helpers.PredictComponent(text, config))
-
-  def testPredict_UnknownComponentIndex(self):
-    """Test case where the prediction is not in components_by_index."""
-    config = self.services.config.GetProjectConfig(
-        None, self.project.project_id)
-
-    self._top_words = {
-        'foo': 0,
-        'bar': 1,
-        'baz': 2}
-    self._components_by_index = {
-        '0': '123',
-        '1': '456',
-        '2': '789'}
-    self._ml_engine.expected_features = [3, 0, 1, 0, 0]
-    self._ml_engine.scores = [5, 10, 3, 1000]
-
-    text = 'foo baz foo foo'
-
-    self.assertIsNone(component_helpers.PredictComponent(text, config))
-
-  def testPredict_InvalidComponentIndex(self):
-    """Test case where the prediction is not a valid component id."""
-    config = self.services.config.GetProjectConfig(
-        None, self.project.project_id)
-
-    self._top_words = {
-        'foo': 0,
-        'bar': 1,
-        'baz': 2}
-    self._components_by_index = {
-        '0': '123',
-        '1': '456',
-        '2': '789'}
-    self._ml_engine.expected_features = [3, 0, 1, 0, 0]
-    self._ml_engine.scores = [5, 10, 3]
-
-    text = 'foo baz foo foo'
-
-    self.assertIsNone(component_helpers.PredictComponent(text, config))
diff --git a/features/test/componentexport_test.py b/features/test/componentexport_test.py
deleted file mode 100644
index 0e5fbf8..0000000
--- a/features/test/componentexport_test.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# 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 the componentexport module."""
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import mock
-import unittest
-import webapp2
-
-import settings
-from features import componentexport
-from framework import urls
-
-
-class ComponentTrainingDataExportTest(unittest.TestCase):
-
-  def test_handler_definition(self):
-    instance = componentexport.ComponentTrainingDataExport()
-    self.assertIsInstance(instance, webapp2.RequestHandler)
-
-  @mock.patch('framework.cloud_tasks_helpers._get_client')
-  def test_enqueues_task(self, get_client_mock):
-    componentexport.ComponentTrainingDataExport().get()
-
-    queue = 'componentexport'
-    task = {
-        'app_engine_http_request':
-            {
-                'http_method': 'GET',
-                'relative_uri': urls.COMPONENT_DATA_EXPORT_TASK
-            }
-    }
-
-    get_client_mock().queue_path.assert_called_with(
-        settings.app_id, settings.CLOUD_TASKS_REGION, queue)
-    get_client_mock().create_task.assert_called_once()
-    ((_parent, called_task), _kwargs) = get_client_mock().create_task.call_args
-    self.assertEqual(called_task, task)
diff --git a/features/test/filterrules_helpers_test.py b/features/test/filterrules_helpers_test.py
index 99d22b7..a68c279 100644
--- a/features/test/filterrules_helpers_test.py
+++ b/features/test/filterrules_helpers_test.py
@@ -10,8 +10,8 @@
 
 import mock
 import unittest
-import urllib
-import urlparse
+from six.moves import urllib
+from six.moves.urllib.parse import parse_qs
 
 import settings
 from features import filterrules_helpers
@@ -139,7 +139,7 @@
           'app_engine_http_request':
               {
                   'relative_uri': urls.RECOMPUTE_DERIVED_FIELDS_TASK + '.do',
-                  'body': urllib.urlencode(params),
+                  'body': urllib.parse.urlencode(params),
                   'headers':
                       {
                           'Content-type': 'application/x-www-form-urlencoded'
@@ -177,7 +177,7 @@
         'relative_uri')
     self.assertEqual(relative_uri, urls.RECOMPUTE_DERIVED_FIELDS_TASK + '.do')
     encoded_params = called_task.get('app_engine_http_request').get('body')
-    params = {k: v[0] for k, v in urlparse.parse_qs(encoded_params).items()}
+    params = {k: v[0] for k, v in parse_qs(encoded_params).items()}
     self.assertEqual(params['project_id'], str(self.project.project_id))
     self.assertEqual(
         params['lower_bound'], str(12345 // self.BLOCK * self.BLOCK + 1))
@@ -188,7 +188,7 @@
         'relative_uri')
     self.assertEqual(relative_uri, urls.RECOMPUTE_DERIVED_FIELDS_TASK + '.do')
     encoded_params = called_task.get('app_engine_http_request').get('body')
-    params = {k: v[0] for k, v in urlparse.parse_qs(encoded_params).items()}
+    params = {k: v[0] for k, v in parse_qs(encoded_params).items()}
     self.assertEqual(params['project_id'], str(self.project.project_id))
     self.assertEqual(params['lower_bound'], str(1))
     self.assertEqual(params['upper_bound'], str(self.BLOCK + 1))
diff --git a/features/test/inboundemail_test.py b/features/test/inboundemail_test.py
index 6c13827..0eaa281 100644
--- a/features/test/inboundemail_test.py
+++ b/features/test/inboundemail_test.py
@@ -15,6 +15,7 @@
 import mox
 import time
 
+from google.appengine.api import mail
 from google.appengine.ext.webapp.mail_handlers import BounceNotificationHandler
 
 import settings
@@ -36,7 +37,6 @@
 
 
 class InboundEmailTest(unittest.TestCase):
-
   def setUp(self):
     self.cnxn = 'fake cnxn'
     self.services = service_manager.Services(
@@ -358,28 +358,6 @@
     self.mox.UnsetStubs()
     self.mox.ResetAll()
 
-  def testPost_Normal(self):
-    """Normally, our post() just calls BounceNotificationHandler post()."""
-    self.mox.StubOutWithMock(BounceNotificationHandler, 'post')
-    BounceNotificationHandler.post()
-    self.mox.ReplayAll()
-
-    self.servlet.post()
-    self.mox.VerifyAll()
-
-  def testPost_Exception(self):
-    """Our post() method works around an escaping bug."""
-    self.servlet.request = webapp2.Request.blank(
-        '/', POST={'raw-message': 'this is an email message'})
-
-    self.mox.StubOutWithMock(BounceNotificationHandler, 'post')
-    BounceNotificationHandler.post().AndRaise(AttributeError())
-    BounceNotificationHandler.post()
-    self.mox.ReplayAll()
-
-    self.servlet.post()
-    self.mox.VerifyAll()
-
   def testReceive_Normal(self):
     """Find the user that bounced and set email_bounce_timestamp."""
     self.assertEqual(0, self.user.email_bounce_timestamp)
diff --git a/features/test/notify_test.py b/features/test/notify_test.py
index 00de106..9ddcce7 100644
--- a/features/test/notify_test.py
+++ b/features/test/notify_test.py
@@ -26,8 +26,6 @@
 from tracker import attachment_helpers
 from tracker import tracker_bizobj
 
-from third_party import cloudstorage
-
 
 def MakeTestIssue(project_id, local_id, owner_id, reporter_id, is_spam=False):
   issue = tracker_pb2.Issue()
@@ -62,8 +60,6 @@
         project_id=12345, local_id=2, owner_id=2, reporter_id=1)
     self.services.issue.TestAddIssue(self.issue1)
 
-    self._old_gcs_open = cloudstorage.open
-    cloudstorage.open = fake.gcs_open
     self.orig_sign_attachment_id = attachment_helpers.SignAttachmentID
     attachment_helpers.SignAttachmentID = (
         lambda aid: 'signed_%d' % aid)
@@ -74,7 +70,6 @@
     self.testbed.init_datastore_v3_stub()
 
   def tearDown(self):
-    cloudstorage.open = self._old_gcs_open
     attachment_helpers.SignAttachmentID = self.orig_sign_attachment_id
 
   def get_filtered_task_call_args(self, create_task_mock, relative_uri):
diff --git a/features/test/send_notifications_test.py b/features/test/send_notifications_test.py
index 435a67d..b15fb23 100644
--- a/features/test/send_notifications_test.py
+++ b/features/test/send_notifications_test.py
@@ -10,7 +10,7 @@
 
 import mock
 import unittest
-import urlparse
+from six.moves.urllib.parse import parse_qs
 
 from features import send_notifications
 from framework import urls
@@ -31,9 +31,7 @@
     (args, _kwargs) = call
     path = args[0]['app_engine_http_request']['relative_uri']
     encoded_params = args[0]['app_engine_http_request']['body']
-    params = {
-        k: v[0] for k, v in urlparse.parse_qs(encoded_params, True).items()
-    }
+    params = {k: v[0] for k, v in parse_qs(encoded_params, True).items()}
     return path, params
 
   @mock.patch('framework.cloud_tasks_helpers.create_task')
diff --git a/features/test/spammodel_test.py b/features/test/spammodel_test.py
deleted file mode 100644
index 3e99c8f..0000000
--- a/features/test/spammodel_test.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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 the spammodel module."""
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import mock
-import unittest
-import webapp2
-
-from features import spammodel
-from framework import urls
-
-
-class TrainingDataExportTest(unittest.TestCase):
-
-  def test_handler_definition(self):
-    instance = spammodel.TrainingDataExport()
-    self.assertIsInstance(instance, webapp2.RequestHandler)
-
-  @mock.patch('framework.cloud_tasks_helpers._get_client')
-  def test_enqueues_task(self, get_client_mock):
-    spammodel.TrainingDataExport().get()
-    task = {
-        'app_engine_http_request':
-            {
-                'relative_uri': urls.SPAM_DATA_EXPORT_TASK + '.do',
-                'body': '',
-                'headers': {
-                    'Content-type': 'application/x-www-form-urlencoded'
-                }
-            }
-    }
-    get_client_mock().create_task.assert_called_once()
-    ((_parent, called_task), _kwargs) = get_client_mock().create_task.call_args
-    self.assertEqual(called_task, task)