chromium-dashboard/internals/notifier_test.py

735 строки
30 KiB
Python

# Copyright 2020 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License")
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import collections
import json
import testing_config # Must be imported before the module under test.
from datetime import datetime
import flask
from unittest import mock
import werkzeug.exceptions # Flask HTTP stuff.
from google.cloud import ndb # type: ignore
from framework import users
from internals import approval_defs
from internals import core_enums
from internals import core_models
from internals import notifier
from internals import user_models
import settings
test_app = flask.Flask(__name__,
template_folder=settings.get_flask_template_path())
# Load testdata to be used across all of the CustomTestCases
TESTDATA = testing_config.Testdata(__file__)
class EmailFormattingTest(testing_config.CustomTestCase):
def setUp(self):
self.feature_1 = core_models.Feature(
name='feature one', summary='sum', owner=['feature_owner@example.com'],
ot_milestone_desktop_start=100,
editors=['feature_editor@example.com', 'owner_1@example.com'],
cc_recipients=['cc@example.com'], category=1,
created_by=ndb.User(
email='creator1@gmail.com', _auth_domain='gmail.com'),
updated_by=ndb.User(
email='editor1@gmail.com', _auth_domain='gmail.com'),
blink_components=['Blink'])
self.feature_1.put()
self.fe_1 = core_models.FeatureEntry(
id=self.feature_1.key.integer_id(),
name='feature one', summary='sum',
owner_emails=['feature_owner@example.com'],
#ot_milestone_desktop_start=100,
editor_emails=['feature_editor@example.com', 'owner_1@example.com'],
cc_emails=['cc@example.com'], category=1,
creator_email='creator1@gmail.com',
updater_email='editor1@gmail.com',
blink_components=['Blink'])
self.fe_1.put()
self.component_1 = user_models.BlinkComponent(name='Blink')
self.component_1.put()
self.component_owner_1 = user_models.FeatureOwner(
name='owner_1', email='owner_1@example.com',
primary_blink_components=[self.component_1.key])
self.component_owner_1.put()
self.watcher_1 = user_models.FeatureOwner(
name='watcher_1', email='watcher_1@example.com',
watching_all_features=True)
self.watcher_1.put()
self.changes = [dict(prop_name='test_prop', new_val='test new value',
old_val='test old value')]
self.feature_2 = core_models.Feature(
name='feature two', summary='sum', owner=['feature_owner@example.com'],
editors=['feature_editor@example.com', 'owner_1@example.com'],
category=1, created_by=ndb.User(
email='creator2@example.com', _auth_domain='gmail.com'),
updated_by=ndb.User(
email='editor2@example.com', _auth_domain='gmail.com'),
blink_components=['Blink'])
self.feature_2.put()
self.fe_2 = core_models.FeatureEntry(
id=self.feature_2.key.integer_id(),
name='feature two', summary='sum',
owner_emails=['feature_owner@example.com'],
editor_emails=['feature_editor@example.com', 'owner_1@example.com'],
category=1,
creator_email='creator2@example.com',
updater_email='editor2@example.com',
blink_components=['Blink'])
self.fe_2.put()
# This feature will only be used for the template tests.
# Hardcode the Feature Key ID so that the ID is deterministic in the
# template tests.
self.template_feature = core_models.Feature(
name='feature template', summary='sum', owner=['feature_owner@example.com'],
editors=['feature_editor@example.com', 'owner_1@example.com'],
category=1, created_by=ndb.User(
email='creator_template@example.com', _auth_domain='gmail.com'),
updated_by=ndb.User(
email='editor_template@example.com', _auth_domain='gmail.com'),
blink_components=['Blink'])
self.template_feature.key = ndb.Key('Feature', 123)
self.template_feature.put()
self.maxDiff = None
def tearDown(self):
self.watcher_1.key.delete()
self.component_owner_1.key.delete()
self.component_1.key.delete()
self.feature_1.key.delete()
self.feature_2.key.delete()
self.fe_1.key.delete()
self.fe_2.key.delete()
self.template_feature.key.delete()
def test_format_email_body__new(self):
"""We generate an email body for new features."""
with test_app.app_context():
body_html = notifier.format_email_body(
False, self.template_feature, [])
# TESTDATA.make_golden(body_html, 'test_format_email_body__new.html')
self.assertEqual(body_html,
TESTDATA['test_format_email_body__new.html'])
def test_format_email_body__update_no_changes(self):
"""We don't crash if the change list is emtpy."""
with test_app.app_context():
body_html = notifier.format_email_body(
True, self.template_feature, [])
# TESTDATA.make_golden(body_html, 'test_format_email_body__update_no_changes.html')
self.assertEqual(body_html,
TESTDATA['test_format_email_body__update_no_changes.html'])
def test_format_email_body__update_with_changes(self):
"""We generate an email body for an updated feature."""
with test_app.app_context():
body_html = notifier.format_email_body(
True, self.template_feature, self.changes)
# TESTDATA.make_golden(body_html, 'test_format_email_body__update_with_changes.html')
self.assertEqual(body_html,
TESTDATA['test_format_email_body__update_with_changes.html'])
def test_format_email_body__mozdev_links(self):
"""We generate an email body with links to developer.mozilla.org."""
self.feature_1.doc_links = ['https://developer.mozilla.org/look-here']
with test_app.app_context():
body_html = notifier.format_email_body(
True, self.template_feature, self.changes)
# TESTDATA.make_golden(body_html, 'test_format_email_body__mozdev_links_mozilla.html')
self.assertEqual(body_html,
TESTDATA['test_format_email_body__mozdev_links_mozilla.html'])
self.feature_1.doc_links = [
'https://hacker-site.org/developer.mozilla.org/look-here']
with test_app.app_context():
body_html = notifier.format_email_body(
True, self.template_feature, self.changes)
# TESTDATA.make_golden(body_html, 'test_format_email_body__mozdev_links_non_mozilla.html')
self.assertEqual(body_html,
TESTDATA['test_format_email_body__mozdev_links_non_mozilla.html'])
def test_accumulate_reasons(self):
"""We can accumulate lists of reasons why we sent a message to a user."""
addr_reasons = collections.defaultdict(list)
# Adding an empty list of users
notifier.accumulate_reasons(addr_reasons, [], 'a reason')
self.assertEqual({}, addr_reasons)
# Adding some users builds up a bigger reason dictionary.
notifier.accumulate_reasons(
addr_reasons, [self.component_owner_1.email], 'a reason')
self.assertEqual(
{'owner_1@example.com': ['a reason']},
addr_reasons)
notifier.accumulate_reasons(
addr_reasons, [self.component_owner_1.email, self.watcher_1.email],
'another reason')
self.assertEqual(
{'owner_1@example.com': ['a reason', 'another reason'],
'watcher_1@example.com': ['another reason'],
},
addr_reasons)
# We can also add email addresses that are not users.
notifier.accumulate_reasons(
addr_reasons, ['mailing-list@example.com'], 'third reason')
self.assertEqual(
{'owner_1@example.com': ['a reason', 'another reason'],
'watcher_1@example.com': ['another reason'],
'mailing-list@example.com': ['third reason'],
},
addr_reasons)
def test_convert_reasons_to_task__no_reasons(self):
with self.assertRaises(AssertionError):
notifier.convert_reasons_to_task(
'addr', [], 'html', 'subject', 'triggerer')
def test_convert_reasons_to_task__normal(self):
actual = notifier.convert_reasons_to_task(
'addr', ['reason 1', 'reason 2'], 'html', 'subject',
'triggerer@example.com')
self.assertCountEqual(
['to', 'subject', 'html', 'reply_to'],
list(actual.keys()))
self.assertEqual('addr', actual['to'])
self.assertEqual('subject', actual['subject'])
self.assertEqual(None, actual['reply_to']) # Lacks perm to reply.
self.assertIn('html', actual['html'])
self.assertIn('reason 1', actual['html'])
self.assertIn('reason 2', actual['html'])
def test_convert_reasons_to_task__can_reply(self):
"""If the user is allowed to reply, set reply_to to the triggerer."""
actual = notifier.convert_reasons_to_task(
'user@chromium.org', ['reason 1', 'reason 2'], 'html', 'subject',
'triggerer@example.com')
self.assertCountEqual(
['to', 'subject', 'html', 'reply_to'],
list(actual.keys()))
self.assertEqual('user@chromium.org', actual['to'])
self.assertEqual('subject', actual['subject'])
self.assertEqual('triggerer@example.com', actual['reply_to'])
def test_apply_subscription_rules__relevant_match(self):
"""When a feature and change match a rule, a reason is returned."""
self.feature_1.shipped_android_milestone = 88
changes = [{'prop_name': 'shipped_android_milestone'}]
actual = notifier.apply_subscription_rules(self.feature_1, changes)
self.assertEqual(
{notifier.WEBVIEW_RULE_REASON: notifier.WEBVIEW_RULE_ADDRS},
actual)
def test_apply_subscription_rules__irrelevant_match(self):
"""When a feature matches, but the change is not relevant => skip."""
self.feature_1.shipped_android_milestone = 88
changes = [{'prop_name': 'some_other_field'}] # irrelevant changesa
actual = notifier.apply_subscription_rules(self.feature_1, changes)
self.assertEqual({}, actual)
def test_apply_subscription_rules__non_match(self):
"""When a feature is not a match => skip."""
changes = [{'prop_name': 'shipped_android_milestone'}]
# No milestones of any kind set.
actual = notifier.apply_subscription_rules(self.feature_1, changes)
self.assertEqual({}, actual)
# Webview is also set
self.feature_1.shipped_android_milestone = 88
self.feature_1.shipped_webview_milestone = 89
actual = notifier.apply_subscription_rules(self.feature_1, changes)
self.assertEqual({}, actual)
@mock.patch('internals.notifier.format_email_body')
def test_make_email_tasks__new(self, mock_f_e_b):
"""We send email to component owners and subscribers for new features."""
mock_f_e_b.return_value = 'mock body html'
actual_tasks = notifier.make_email_tasks(
self.feature_1, is_update=False, changes=[])
self.assertEqual(5, len(actual_tasks))
(feature_cc_task, feature_editor_task, feature_owner_task,
component_owner_task, watcher_task) = actual_tasks
# Notification to feature owner.
self.assertEqual('feature_owner@example.com', feature_owner_task['to'])
self.assertEqual('new feature: feature one', feature_owner_task['subject'])
self.assertIn('mock body html', feature_owner_task['html'])
self.assertIn('<li>You are listed as an owner of this feature</li>',
feature_owner_task['html'])
# Notification to feature editor.
self.assertEqual('new feature: feature one', feature_editor_task['subject'])
self.assertIn('mock body html', feature_editor_task['html'])
self.assertIn('<li>You are listed as an editor of this feature</li>',
feature_editor_task['html'])
self.assertEqual('feature_editor@example.com', feature_editor_task['to'])
# Notification to user CC'd to feature changes.
self.assertEqual('new feature: feature one', feature_cc_task['subject'])
self.assertIn('mock body html', feature_cc_task['html'])
self.assertIn('<li>You are CC\'d on this feature</li>',
feature_cc_task['html'])
self.assertEqual('cc@example.com', feature_cc_task['to'])
# Notification to component owner.
self.assertEqual('new feature: feature one', component_owner_task['subject'])
self.assertIn('mock body html', component_owner_task['html'])
# Component owner is also a feature editor and should have both reasons.
self.assertIn('<li>You are an owner of this feature\'s component</li>\n'
'<li>You are listed as an editor of this feature</li>',
component_owner_task['html'])
self.assertEqual('owner_1@example.com', component_owner_task['to'])
# Notification to feature change watcher.
self.assertEqual('new feature: feature one', watcher_task['subject'])
self.assertIn('mock body html', watcher_task['html'])
self.assertIn('<li>You are watching all feature changes</li>',
watcher_task['html'])
self.assertEqual('watcher_1@example.com', watcher_task['to'])
mock_f_e_b.assert_called_once_with(
False, self.feature_1, [])
@mock.patch('internals.notifier.format_email_body')
def test_make_email_tasks__update(self, mock_f_e_b):
"""We send email to component owners and subscribers for edits."""
mock_f_e_b.return_value = 'mock body html'
actual_tasks = notifier.make_email_tasks(
self.feature_1, True, self.changes)
self.assertEqual(5, len(actual_tasks))
(feature_cc_task, feature_editor_task, feature_owner_task,
component_owner_task, watcher_task) = actual_tasks
# Notification to feature owner.
self.assertEqual('feature_owner@example.com', feature_owner_task['to'])
self.assertEqual('updated feature: feature one',
feature_owner_task['subject'])
self.assertIn('mock body html', feature_owner_task['html'])
self.assertIn('<li>You are listed as an owner of this feature</li>',
feature_owner_task['html'])
# Notification to feature editor.
self.assertEqual('updated feature: feature one',
feature_editor_task['subject'])
self.assertIn('mock body html', feature_editor_task['html'])
self.assertIn('<li>You are listed as an editor of this feature</li>',
feature_editor_task['html'])
self.assertEqual('feature_editor@example.com', feature_editor_task['to'])
# Notification to user CC'd on feature changes.
self.assertEqual('updated feature: feature one',
feature_cc_task['subject'])
self.assertIn('mock body html', feature_cc_task['html'])
self.assertIn('<li>You are CC\'d on this feature</li>',
feature_cc_task['html'])
self.assertEqual('cc@example.com', feature_cc_task['to'])
# Notification to component owner.
self.assertEqual('updated feature: feature one',
component_owner_task['subject'])
self.assertIn('mock body html', component_owner_task['html'])
# Component owner is also a feature editor and should have both reasons.
self.assertIn('<li>You are an owner of this feature\'s component</li>\n'
'<li>You are listed as an editor of this feature</li>',
component_owner_task['html'])
self.assertEqual('owner_1@example.com', component_owner_task['to'])
# Notification to feature change watcher.
self.assertEqual('updated feature: feature one', watcher_task['subject'])
self.assertIn('mock body html', watcher_task['html'])
self.assertIn('<li>You are watching all feature changes</li>',
watcher_task['html'])
self.assertEqual('watcher_1@example.com', watcher_task['to'])
mock_f_e_b.assert_called_once_with(
True, self.feature_1, self.changes)
@mock.patch('internals.notifier.format_email_body')
def test_make_email_tasks__starrer(self, mock_f_e_b):
"""We send email to users who starred the feature."""
mock_f_e_b.return_value = 'mock body html'
notifier.FeatureStar.set_star(
'starrer_1@example.com', self.feature_1.key.integer_id())
actual_tasks = notifier.make_email_tasks(
self.feature_1, True, self.changes)
self.assertEqual(6, len(actual_tasks))
(feature_cc_task, feature_editor_task, feature_owner_task,
component_owner_task, starrer_task, watcher_task) = actual_tasks
# Notification to feature owner.
self.assertEqual('feature_owner@example.com', feature_owner_task['to'])
self.assertEqual('updated feature: feature one',
feature_owner_task['subject'])
self.assertIn('mock body html', feature_owner_task['html'])
self.assertIn('<li>You are listed as an owner of this feature</li>',
feature_owner_task['html'])
# Notification to feature editor.
self.assertEqual('updated feature: feature one',
feature_editor_task['subject'])
self.assertIn('mock body html', feature_editor_task['html'])
self.assertIn('<li>You are listed as an editor of this feature</li>',
feature_editor_task['html'])
self.assertEqual('feature_editor@example.com', feature_editor_task['to'])
# Notification to user CC'd on feature changes.
self.assertEqual('updated feature: feature one',
feature_cc_task['subject'])
self.assertIn('mock body html', feature_cc_task['html'])
self.assertIn('<li>You are CC\'d on this feature</li>',
feature_cc_task['html'])
self.assertEqual('cc@example.com', feature_cc_task['to'])
# Notification to component owner.
self.assertEqual('updated feature: feature one',
component_owner_task['subject'])
self.assertIn('mock body html', component_owner_task['html'])
# Component owner is also a feature editor and should have both reasons.
self.assertIn('<li>You are an owner of this feature\'s component</li>\n'
'<li>You are listed as an editor of this feature</li>',
component_owner_task['html'])
self.assertEqual('owner_1@example.com', component_owner_task['to'])
# Notification to feature starrer.
self.assertEqual('updated feature: feature one', starrer_task['subject'])
self.assertIn('mock body html', starrer_task['html'])
self.assertIn('<li>You starred this feature</li>',
starrer_task['html'])
self.assertEqual('starrer_1@example.com', starrer_task['to'])
# Notification to feature change watcher.
self.assertEqual('updated feature: feature one', watcher_task['subject'])
self.assertIn('mock body html', watcher_task['html'])
self.assertIn('<li>You are watching all feature changes</li>',
watcher_task['html'])
self.assertEqual('watcher_1@example.com', watcher_task['to'])
mock_f_e_b.assert_called_once_with(
True, self.feature_1, self.changes)
@mock.patch('internals.notifier.format_email_body')
def test_make_email_tasks__starrer_unsubscribed(self, mock_f_e_b):
"""We don't email users who starred the feature but opted out."""
mock_f_e_b.return_value = 'mock body html'
starrer_2_pref = user_models.UserPref(
email='starrer_2@example.com',
notify_as_starrer=False)
starrer_2_pref.put()
notifier.FeatureStar.set_star(
'starrer_2@example.com', self.feature_2.key.integer_id())
actual_tasks = notifier.make_email_tasks(
self.feature_2, True, self.changes)
self.assertEqual(4, len(actual_tasks))
# Note: There is no starrer_task.
(feature_editor_task, feature_owner_task, component_owner_task,
watcher_task) = actual_tasks
self.assertEqual('feature_editor@example.com', feature_editor_task['to'])
self.assertEqual('feature_owner@example.com', feature_owner_task['to'])
self.assertEqual('owner_1@example.com', component_owner_task['to'])
self.assertEqual('watcher_1@example.com', watcher_task['to'])
mock_f_e_b.assert_called_once_with(
True, self.feature_2, self.changes)
class FeatureStarTest(testing_config.CustomTestCase):
def setUp(self):
self.feature_1 = core_models.Feature(
name='feature one', summary='sum', category=1)
self.feature_1.put()
self.feature_2 = core_models.Feature(
name='feature two', summary='sum', category=1)
self.feature_2.put()
self.feature_3 = core_models.Feature(
name='feature three', summary='sum', category=1)
self.feature_3.put()
self.fe_1 = core_models.FeatureEntry(
id=self.feature_1.key.integer_id(),
name='feature one', summary='sum', category=1)
self.fe_1.put()
self.fe_2 = core_models.FeatureEntry(
id=self.feature_2.key.integer_id(),
name='feature two', summary='sum', category=1)
self.fe_2.put()
self.fe_3 = core_models.FeatureEntry(
id=self.feature_3.key.integer_id(),
name='feature three', summary='sum', category=1)
self.fe_3.put()
def tearDown(self):
self.feature_1.key.delete()
self.feature_2.key.delete()
self.feature_3.key.delete()
self.fe_1.key.delete()
self.fe_2.key.delete()
self.fe_3.key.delete()
def test_get_star__no_existing(self):
"""User has never starred the given feature."""
email = 'user1@example.com'
feature_id = self.feature_1.key.integer_id()
actual = notifier.FeatureStar.get_star(email, feature_id)
self.assertEqual(None, actual)
def test_get_and_set_star(self):
"""User can star and unstar a feature."""
email = 'user2@example.com'
feature_id = self.feature_1.key.integer_id()
notifier.FeatureStar.set_star(email, feature_id)
actual = notifier.FeatureStar.get_star(email, feature_id)
self.assertEqual(email, actual.email)
self.assertEqual(feature_id, actual.feature_id)
self.assertTrue(actual.starred)
updated_feature = core_models.Feature.get_by_id(feature_id)
self.assertEqual(1, updated_feature.star_count)
updated_fe = core_models.FeatureEntry.get_by_id(feature_id)
self.assertEqual(1, updated_fe.star_count)
notifier.FeatureStar.set_star(email, feature_id, starred=False)
actual = notifier.FeatureStar.get_star(email, feature_id)
self.assertEqual(email, actual.email)
self.assertEqual(feature_id, actual.feature_id)
self.assertFalse(actual.starred)
updated_feature = core_models.Feature.get_by_id(feature_id)
self.assertEqual(0, updated_feature.star_count)
updated_fe = core_models.FeatureEntry.get_by_id(feature_id)
self.assertEqual(0, updated_fe.star_count)
def test_get_user_stars__no_stars(self):
"""User has never starred any features."""
email = 'user4@example.com'
with test_app.app_context():
actual = notifier.FeatureStar.get_user_stars(email)
self.assertEqual([], actual)
def test_get_user_stars__some_stars(self):
"""User has starred three features."""
email = 'user5@example.com'
feature_1_id = self.feature_1.key.integer_id()
feature_2_id = self.feature_2.key.integer_id()
feature_3_id = self.feature_3.key.integer_id()
# Note intermixed order
notifier.FeatureStar.set_star(email, feature_1_id)
notifier.FeatureStar.set_star(email, feature_3_id)
notifier.FeatureStar.set_star(email, feature_2_id)
actual = notifier.FeatureStar.get_user_stars(email)
expected_ids = [feature_1_id, feature_2_id, feature_3_id]
self.assertEqual(sorted(expected_ids, reverse=True), actual)
# Cleanup
for feature_id in expected_ids:
notifier.FeatureStar.get_star(email, feature_id).key.delete()
def test_get_feature_starrers__no_stars(self):
"""No user has starred the given feature."""
feature_1_id = self.feature_1.key.integer_id()
actual = notifier.FeatureStar.get_feature_starrers(feature_1_id)
self.assertEqual([], actual)
def test_get_feature_starrers__some_starrers(self):
"""Two users have starred the given feature."""
app_user_1 = user_models.AppUser(email='user16@example.com')
app_user_1.put()
app_user_2 = user_models.AppUser(email='user17@example.com')
app_user_2.put()
feature_1_id = self.feature_1.key.integer_id()
notifier.FeatureStar.set_star(app_user_1.email, feature_1_id)
notifier.FeatureStar.set_star(app_user_2.email, feature_1_id)
user_1_email = app_user_1.email
user_2_email = app_user_2.email
app_user_1.key.delete()
app_user_2.key.delete()
actual = notifier.FeatureStar.get_feature_starrers(feature_1_id)
self.assertCountEqual(
[user_1_email, user_2_email],
[au.email for au in actual])
class NotifyInactiveUsersHandlerTest(testing_config.CustomTestCase):
def setUp(self):
self.users = []
active_user = user_models.AppUser(
email="active_user@example.com", is_admin=False, is_site_editor=False,
last_visit=datetime(2023, 8, 30))
active_user.put()
self.users.append(active_user)
self.inactive_user = user_models.AppUser(
email="inactive_user@example.com", is_admin=False, is_site_editor=False,
last_visit=datetime(2023, 2, 20))
self.inactive_user.put()
self.users.append(self.inactive_user)
really_inactive_user = user_models.AppUser(
email="really_inactive_user@example.com", is_admin=False,
is_site_editor=False, last_visit=datetime(2022, 10, 1),
notified_inactive=True)
really_inactive_user.put()
self.users.append(really_inactive_user)
active_admin = user_models.AppUser(
email="active_admin@example.com", is_admin=True, is_site_editor=True,
last_visit=datetime(2023, 9, 30))
active_admin.put()
self.users.append(active_admin)
inactive_admin = user_models.AppUser(
email="inactive_admin@example.com", is_admin=True, is_site_editor=True,
last_visit=datetime(2023, 3, 1))
inactive_admin.put()
self.users.append(inactive_admin)
active_site_editor = user_models.AppUser(
email="active_site_editor@example.com", is_admin=False,
is_site_editor=True, last_visit=datetime(2023, 7, 30))
active_site_editor.put()
self.users.append(active_site_editor)
inactive_site_editor = user_models.AppUser(
email="inactive_site_editor@example.com", is_admin=False,
is_site_editor=True, last_visit=datetime(2023, 2, 9))
inactive_site_editor.put()
self.users.append(inactive_site_editor)
def tearDown(self):
for user in self.users:
user.key.delete()
def test_determine_users_to_notify(self):
with test_app.app_context():
inactive_notifier = notifier.NotifyInactiveUsersHandler()
result = inactive_notifier.get_template_data(now=datetime(2023, 9, 1))
expected = ('1 users notified of inactivity.\n'
'Notified users:\ninactive_user@example.com')
self.assertEqual(result.get('message', None), expected)
# The inactive user who was notified should be flagged as notified.
self.assertTrue(self.inactive_user.notified_inactive)
class FunctionsTest(testing_config.CustomTestCase):
def setUp(self):
quoted_msg_id = 'xxx%3Dyyy%40mail.gmail.com'
impl_url = notifier.BLINK_DEV_ARCHIVE_URL_PREFIX + '123' + quoted_msg_id
expr_url = notifier.TEST_ARCHIVE_URL_PREFIX + '456' + quoted_msg_id
self.feature_1 = core_models.Feature(
name='feature one', summary='sum', category=1,
intent_to_implement_url=impl_url, intent_to_experiment_url=expr_url)
# Note: There is no need to put() it in the datastore.
def test_get_thread_id__normal(self):
"""We can select the correct approval thread field of a feature."""
self.assertEqual(
'123xxx=yyy@mail.gmail.com',
notifier.get_thread_id(
self.feature_1, approval_defs.PrototypeApproval))
self.assertEqual(
'456xxx=yyy@mail.gmail.com',
notifier.get_thread_id(
self.feature_1, approval_defs.ExperimentApproval))
self.assertEqual(
None,
notifier.get_thread_id(
self.feature_1, approval_defs.ShipApproval))
def test_get_existing_thread_subject__none(self):
"""If a feature does not store an existing thread subject, use None."""
self.assertIsNone(notifier.get_existing_thread_subject(
self.feature_1, approval_defs.PrototypeApproval))
def test_get_existing_thread_subject__found(self):
"""If a feature does not store an existing thread subject, use it."""
self.feature_1.intent_to_ship_subject_line = (
'Intent to really ship: feature one')
actual = notifier.get_existing_thread_subject(
self.feature_1, approval_defs.ShipApproval)
self.assertEqual('Intent to really ship: feature one', actual)
def test_get_existing_thread_subject__unknown(self):
"""Raise ValueError if called with an unknown approval field."""
PivotApproval = approval_defs.ApprovalFieldDef(
'Intent to Pivot', 'API Owners',
'One API Owner must approve your intent',
99, approval_defs.ONE_LGTM, [])
with self.assertRaises(ValueError):
notifier.get_existing_thread_subject(
self.feature_1, PivotApproval)
def test_generate_thread_subject__normal(self):
"""Most intents just use the name of the intent."""
self.assertEqual(
'Intent to Prototype: feature one',
notifier.generate_thread_subject(
self.feature_1, approval_defs.PrototypeApproval))
self.assertEqual(
'Intent to Experiment: feature one',
notifier.generate_thread_subject(
self.feature_1, approval_defs.ExperimentApproval))
self.assertEqual(
'Intent to Extend Experiment: feature one',
notifier.generate_thread_subject(
self.feature_1, approval_defs.ExtendExperimentApproval))
self.assertEqual(
'Intent to Ship: feature one',
notifier.generate_thread_subject(
self.feature_1, approval_defs.ShipApproval))
def test_generate_thread_subject__deprecation(self):
"""Deprecation intents use different subjects for most intents."""
self.feature_1.feature_type = core_enums.FEATURE_TYPE_DEPRECATION_ID
self.assertEqual(
'Intent to Deprecate and Remove: feature one',
notifier.generate_thread_subject(
self.feature_1, approval_defs.PrototypeApproval))
self.assertEqual(
'Request for Deprecation Trial: feature one',
notifier.generate_thread_subject(
self.feature_1, approval_defs.ExperimentApproval))
self.assertEqual(
'Intent to Extend Deprecation Trial: feature one',
notifier.generate_thread_subject(
self.feature_1, approval_defs.ExtendExperimentApproval))
self.assertEqual(
'Intent to Ship: feature one',
notifier.generate_thread_subject(
self.feature_1, approval_defs.ShipApproval))
def test_get_thread_id__trailing_junk(self):
"""We can select the correct approval thread field of a feature."""
self.feature_1.intent_to_implement_url += '?param=val#anchor'
self.assertEqual(
'123xxx=yyy@mail.gmail.com',
notifier.get_thread_id(
self.feature_1, approval_defs.PrototypeApproval))