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