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