blob: 09e5c5cdeebbb8996357393a0df2bd189b5c8a5a [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001# Copyright 2016 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file or at
4# https://developers.google.com/open-source/licenses/bsd
5
6"""Unittest for the dateaction module."""
7
8from __future__ import division
9from __future__ import print_function
10from __future__ import absolute_import
11
12import logging
13import mock
14import time
15import unittest
16
17from features import dateaction
18from framework import cloud_tasks_helpers
19from framework import framework_constants
20from framework import framework_views
21from framework import timestr
22from framework import urls
23from proto import tracker_pb2
24from services import service_manager
25from testing import fake
26from testing import testing_helpers
27from tracker import tracker_bizobj
28
29
30NOW = 1492120863
31
32
33class DateActionCronTest(unittest.TestCase):
34
35 def setUp(self):
36 self.services = service_manager.Services(
37 user=fake.UserService(),
38 issue=fake.IssueService())
39 self.servlet = dateaction.DateActionCron(
40 'req', 'res', services=self.services)
41 self.TIMESTAMP_MIN = (
42 NOW // framework_constants.SECS_PER_DAY *
43 framework_constants.SECS_PER_DAY)
44 self.TIMESTAMP_MAX = self.TIMESTAMP_MIN + framework_constants.SECS_PER_DAY
45 self.left_joins = [
46 ('Issue2FieldValue ON Issue.id = Issue2FieldValue.issue_id', []),
47 ('FieldDef ON Issue2FieldValue.field_id = FieldDef.id', []),
48 ]
49 self.where = [
50 ('FieldDef.field_type = %s', ['date_type']),
51 (
52 'FieldDef.date_action IN (%s,%s)',
53 ['ping_owner_only', 'ping_participants']),
54 ('Issue2FieldValue.date_value >= %s', [self.TIMESTAMP_MIN]),
55 ('Issue2FieldValue.date_value < %s', [self.TIMESTAMP_MAX]),
56 ]
57 self.order_by = [
58 ('Issue.id', []),
59 ]
60
61 @mock.patch('time.time', return_value=NOW)
62 def testHandleRequest_NoMatches(self, _mock_time):
63 _request, mr = testing_helpers.GetRequestObjects(
64 path=urls.DATE_ACTION_CRON)
65 self.services.issue.RunIssueQuery = mock.MagicMock(return_value=([], False))
66
67 self.servlet.HandleRequest(mr)
68
69 self.services.issue.RunIssueQuery.assert_called_with(
70 mr.cnxn, self.left_joins, self.where + [('Issue.id > %s', [0])],
71 self.order_by)
72
73 @mock.patch('framework.cloud_tasks_helpers._get_client')
74 @mock.patch('time.time', return_value=NOW)
75 def testHandleRequest_OneMatche(self, _mock_time, get_client_mock):
76 _request, mr = testing_helpers.GetRequestObjects(
77 path=urls.DATE_ACTION_CRON)
78 self.services.issue.RunIssueQuery = mock.MagicMock(
79 return_value=([78901], False))
80
81 self.servlet.HandleRequest(mr)
82
83 self.services.issue.RunIssueQuery.assert_called_with(
84 mr.cnxn, self.left_joins, self.where + [('Issue.id > %s', [0])],
85 self.order_by)
86 expected_task = {
87 'app_engine_http_request':
88 {
89 'relative_uri': urls.ISSUE_DATE_ACTION_TASK + '.do',
90 'body': 'issue_id=78901',
91 'headers': {
92 'Content-type': 'application/x-www-form-urlencoded'
93 }
94 }
95 }
96 get_client_mock().create_task.assert_any_call(
97 get_client_mock().queue_path(),
98 expected_task,
99 retry=cloud_tasks_helpers._DEFAULT_RETRY)
100
101 @mock.patch('framework.cloud_tasks_helpers._get_client')
102 def testEnqueueDateAction(self, get_client_mock):
103 self.servlet.EnqueueDateAction(78901)
104 expected_task = {
105 'app_engine_http_request':
106 {
107 'relative_uri': urls.ISSUE_DATE_ACTION_TASK + '.do',
108 'body': 'issue_id=78901',
109 'headers': {
110 'Content-type': 'application/x-www-form-urlencoded'
111 }
112 }
113 }
114 get_client_mock().create_task.assert_any_call(
115 get_client_mock().queue_path(),
116 expected_task,
117 retry=cloud_tasks_helpers._DEFAULT_RETRY)
118
119
120class IssueDateActionTaskTest(unittest.TestCase):
121
122 def setUp(self):
123 self.services = service_manager.Services(
124 user=fake.UserService(),
125 usergroup=fake.UserGroupService(),
126 features=fake.FeaturesService(),
127 issue=fake.IssueService(),
128 project=fake.ProjectService(),
129 config=fake.ConfigService(),
130 issue_star=fake.IssueStarService())
131 self.servlet = dateaction.IssueDateActionTask(
132 'req', 'res', services=self.services)
133
134 self.config = self.services.config.GetProjectConfig('cnxn', 789)
135 self.config.field_defs = [
136 tracker_bizobj.MakeFieldDef(
137 123, 789, 'NextAction', tracker_pb2.FieldTypes.DATE_TYPE,
138 '', '', False, False, False, None, None, None, False, '',
139 None, None, tracker_pb2.DateAction.PING_OWNER_ONLY,
140 'Date of next expected progress update', False),
141 tracker_bizobj.MakeFieldDef(
142 124, 789, 'EoL', tracker_pb2.FieldTypes.DATE_TYPE,
143 '', '', False, False, False, None, None, None, False, '',
144 None, None, tracker_pb2.DateAction.PING_OWNER_ONLY, 'doc', False),
145 tracker_bizobj.MakeFieldDef(
146 125, 789, 'TLsBirthday', tracker_pb2.FieldTypes.DATE_TYPE,
147 '', '', False, False, False, None, None, None, False, '',
148 None, None, tracker_pb2.DateAction.NO_ACTION, 'doc', False),
149 ]
150 self.services.config.StoreConfig('cnxn', self.config)
151 self.project = self.services.project.TestAddProject('proj', project_id=789)
152 self.owner = self.services.user.TestAddUser('owner@example.com', 111)
153 self.date_action_user = self.services.user.TestAddUser(
154 'date-action-user@example.com', 555)
155
156 def testHandleRequest_IssueHasNoArrivedDates(self):
157 _request, mr = testing_helpers.GetRequestObjects(
158 path=urls.ISSUE_DATE_ACTION_TASK + '.do?issue_id=78901')
159 self.services.issue.TestAddIssue(fake.MakeTestIssue(
160 789, 1, 'summary', 'New', 111, issue_id=78901))
161 self.assertEqual(1, len(self.services.issue.GetCommentsForIssue(
162 mr.cnxn, 78901)))
163
164 self.servlet.HandleRequest(mr)
165 self.assertEqual(1, len(self.services.issue.GetCommentsForIssue(
166 mr.cnxn, 78901)))
167
168 @mock.patch('framework.cloud_tasks_helpers.create_task')
169 def testHandleRequest_IssueHasOneArriveDate(self, create_task_mock):
170 _request, mr = testing_helpers.GetRequestObjects(
171 path=urls.ISSUE_DATE_ACTION_TASK + '.do?issue_id=78901')
172
173 now = int(time.time())
174 date_str = timestr.TimestampToDateWidgetStr(now)
175 issue = fake.MakeTestIssue(789, 1, 'summary', 'New', 111, issue_id=78901)
176 self.services.issue.TestAddIssue(issue)
177 issue.field_values = [
178 tracker_bizobj.MakeFieldValue(123, None, None, None, now, None, False)]
179 self.assertEqual(1, len(self.services.issue.GetCommentsForIssue(
180 mr.cnxn, 78901)))
181
182 self.servlet.HandleRequest(mr)
183 comments = self.services.issue.GetCommentsForIssue(mr.cnxn, 78901)
184 self.assertEqual(2, len(comments))
185 self.assertEqual(
186 'The NextAction date has arrived: %s' % date_str,
187 comments[1].content)
188
189 self.assertEqual(create_task_mock.call_count, 1)
190
191 (args, kwargs) = create_task_mock.call_args
192 self.assertEqual(
193 args[0]['app_engine_http_request']['relative_uri'],
194 urls.OUTBOUND_EMAIL_TASK + '.do')
195 self.assertEqual(kwargs['queue'], 'outboundemail')
196
197 def SetUpFieldValues(self, issue, now):
198 issue.field_values = [
199 tracker_bizobj.MakeFieldValue(123, None, None, None, now, None, False),
200 tracker_bizobj.MakeFieldValue(124, None, None, None, now, None, False),
201 tracker_bizobj.MakeFieldValue(125, None, None, None, now, None, False),
202 ]
203
204 @mock.patch('framework.cloud_tasks_helpers.create_task')
205 def testHandleRequest_IssueHasTwoArriveDates(self, create_task_mock):
206 _request, mr = testing_helpers.GetRequestObjects(
207 path=urls.ISSUE_DATE_ACTION_TASK + '.do?issue_id=78901')
208
209 now = int(time.time())
210 date_str = timestr.TimestampToDateWidgetStr(now)
211 issue = fake.MakeTestIssue(789, 1, 'summary', 'New', 111, issue_id=78901)
212 self.services.issue.TestAddIssue(issue)
213 self.SetUpFieldValues(issue, now)
214 self.assertEqual(1, len(self.services.issue.GetCommentsForIssue(
215 mr.cnxn, 78901)))
216
217 self.servlet.HandleRequest(mr)
218 comments = self.services.issue.GetCommentsForIssue(mr.cnxn, 78901)
219 self.assertEqual(2, len(comments))
220 self.assertEqual(
221 'The EoL date has arrived: %s\n'
222 'The NextAction date has arrived: %s' % (date_str, date_str),
223 comments[1].content)
224
225 self.assertEqual(create_task_mock.call_count, 1)
226
227 (args, kwargs) = create_task_mock.call_args
228 self.assertEqual(
229 args[0]['app_engine_http_request']['relative_uri'],
230 urls.OUTBOUND_EMAIL_TASK + '.do')
231 self.assertEqual(kwargs['queue'], 'outboundemail')
232
233 def MakePingComment(self):
234 comment = tracker_pb2.IssueComment()
235 comment.project_id = self.project.project_id
236 comment.user_id = self.date_action_user.user_id
237 comment.content = 'Some date(s) arrived...'
238 return comment
239
240 def testMakeEmailTasks_Owner(self):
241 """The issue owner gets pinged and the email has expected content."""
242 issue = fake.MakeTestIssue(
243 789, 1, 'summary', 'New', self.owner.user_id, issue_id=78901)
244 self.services.issue.TestAddIssue(issue)
245 now = int(time.time())
246 self.SetUpFieldValues(issue, now)
247 issue.project_name = 'proj'
248 comment = self.MakePingComment()
249 next_action_field_def = self.config.field_defs[0]
250 pings = [(next_action_field_def, now)]
251 users_by_id = framework_views.MakeAllUserViews(
252 'fake cnxn', self.services.user,
253 [self.owner.user_id, self.date_action_user.user_id])
254
255 tasks = self.servlet._MakeEmailTasks(
256 'fake cnxn', issue, self.project, self.config, comment,
257 [], 'example-app.appspot.com', users_by_id, pings)
258 self.assertEqual(1, len(tasks))
259 notify_owner_task = tasks[0]
260 self.assertEqual('owner@example.com', notify_owner_task['to'])
261 self.assertEqual(
262 'Follow up on issue 1 in proj: summary',
263 notify_owner_task['subject'])
264 body = notify_owner_task['body']
265 self.assertIn(comment.content, body)
266 self.assertIn(next_action_field_def.docstring, body)
267
268 def testMakeEmailTasks_Starrer(self):
269 """Users who starred the issue are notified iff they opt in."""
270 issue = fake.MakeTestIssue(
271 789, 1, 'summary', 'New', 0, issue_id=78901)
272 self.services.issue.TestAddIssue(issue)
273 now = int(time.time())
274 self.SetUpFieldValues(issue, now)
275 issue.project_name = 'proj'
276 comment = self.MakePingComment()
277 next_action_field_def = self.config.field_defs[0]
278 pings = [(next_action_field_def, now)]
279
280 starrer_333 = self.services.user.TestAddUser('starrer333@example.com', 333)
281 starrer_333.notify_starred_ping = True
282 self.services.user.TestAddUser('starrer444@example.com', 444)
283 starrer_ids = [333, 444]
284 users_by_id = framework_views.MakeAllUserViews(
285 'fake cnxn', self.services.user,
286 [self.owner.user_id, self.date_action_user.user_id],
287 starrer_ids)
288
289 tasks = self.servlet._MakeEmailTasks(
290 'fake cnxn', issue, self.project, self.config, comment,
291 starrer_ids, 'example-app.appspot.com', users_by_id, pings)
292 self.assertEqual(1, len(tasks))
293 notify_owner_task = tasks[0]
294 self.assertEqual('starrer333@example.com', notify_owner_task['to'])
295
296 def testCalculateIssuePings_Normal(self):
297 """Return a ping for an issue that has a date that happened today."""
298 issue = fake.MakeTestIssue(
299 789, 1, 'summary', 'New', 0, issue_id=78901)
300 self.services.issue.TestAddIssue(issue)
301 now = int(time.time())
302 self.SetUpFieldValues(issue, now)
303 issue.project_name = 'proj'
304
305 pings = self.servlet._CalculateIssuePings(issue, self.config)
306
307 self.assertEqual(
308 [(self.config.field_defs[1], now),
309 (self.config.field_defs[0], now)],
310 pings)
311
312 def testCalculateIssuePings_Closed(self):
313 """Don't ping for a closed issue."""
314 issue = fake.MakeTestIssue(
315 789, 1, 'summary', 'Fixed', 0, issue_id=78901)
316 self.services.issue.TestAddIssue(issue)
317 now = int(time.time())
318 self.SetUpFieldValues(issue, now)
319 issue.project_name = 'proj'
320
321 pings = self.servlet._CalculateIssuePings(issue, self.config)
322
323 self.assertEqual([], pings)