Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(25)

Side by Side Diff: appengine/monorail/services/test/issue_svc_test.py

Issue 1868553004: Open Source Monorail (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Rebase Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is govered 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 """Unit tests for issue_svc module."""
7
8 import time
9 import unittest
10
11 import mox
12
13 from google.appengine.api import search
14 from google.appengine.ext import testbed
15
16 import settings
17 from framework import sql
18 from proto import tracker_pb2
19 from services import issue_svc
20 from services import service_manager
21 from services import spam_svc
22 from services import tracker_fulltext
23 from testing import fake
24 from tracker import tracker_bizobj
25
26
27 class MockIndex(object):
28
29 def delete(self, string_list):
30 pass
31
32
33 def MakeIssueService(project_service, config_service, cache_manager, my_mox):
34 issue_service = issue_svc.IssueService(
35 project_service, config_service, cache_manager)
36 for table_var in [
37 'issue_tbl', 'issuesummary_tbl', 'issue2label_tbl',
38 'issue2component_tbl', 'issue2cc_tbl', 'issue2notify_tbl',
39 'issue2fieldvalue_tbl', 'issuerelation_tbl', 'danglingrelation_tbl',
40 'issueformerlocations_tbl', 'comment_tbl', 'issueupdate_tbl',
41 'attachment_tbl', 'reindexqueue_tbl', 'localidcounter_tbl']:
42 setattr(issue_service, table_var, my_mox.CreateMock(sql.SQLTableManager))
43
44 return issue_service
45
46
47 class IssueIDTwoLevelCacheTest(unittest.TestCase):
48
49 def setUp(self):
50 self.mox = mox.Mox()
51 self.cnxn = 'fake connection'
52 self.project_service = fake.ProjectService()
53 self.config_service = fake.ConfigService()
54 self.cache_manager = fake.CacheManager()
55 self.issue_service = MakeIssueService(
56 self.project_service, self.config_service, self.cache_manager,
57 self.mox)
58 self.issue_id_2lc = self.issue_service.issue_id_2lc
59 self.spam_service = fake.SpamService()
60
61 def tearDown(self):
62 self.mox.UnsetStubs()
63 self.mox.ResetAll()
64
65 def testDeserializeIssueIDs_Empty(self):
66 issue_id_dict = self.issue_id_2lc._DeserializeIssueIDs([])
67 self.assertEqual({}, issue_id_dict)
68
69 def testDeserializeIssueIDs_Normal(self):
70 rows = [(789, 1, 78901), (789, 2, 78902), (789, 3, 78903)]
71 issue_id_dict = self.issue_id_2lc._DeserializeIssueIDs(rows)
72 expected = {
73 (789, 1): 78901,
74 (789, 2): 78902,
75 (789, 3): 78903,
76 }
77 self.assertEqual(expected, issue_id_dict)
78
79 def SetUpFetchItems(self):
80 where = [
81 ('(Issue.project_id = %s AND Issue.local_id IN (%s,%s,%s))',
82 [789, 1, 2, 3])]
83 rows = [(789, 1, 78901), (789, 2, 78902), (789, 3, 78903)]
84 self.issue_service.issue_tbl.Select(
85 self.cnxn, cols=['project_id', 'local_id', 'id'],
86 where=where, or_where_conds=True).AndReturn(rows)
87
88 def testFetchItems(self):
89 project_local_ids_list = [(789, 1), (789, 2), (789, 3)]
90 issue_ids = [78901, 78902, 78903]
91 self.SetUpFetchItems()
92 self.mox.ReplayAll()
93 issue_dict = self.issue_id_2lc.FetchItems(
94 self.cnxn, project_local_ids_list)
95 self.mox.VerifyAll()
96 self.assertItemsEqual(project_local_ids_list, issue_dict.keys())
97 self.assertItemsEqual(issue_ids, issue_dict.values())
98
99 def testKeyToStr(self):
100 self.assertEqual('789,1', self.issue_id_2lc._KeyToStr((789, 1)))
101
102 def testStrToKey(self):
103 self.assertEqual((789, 1), self.issue_id_2lc._StrToKey('789,1'))
104
105
106 class IssueTwoLevelCacheTest(unittest.TestCase):
107
108 def setUp(self):
109 self.mox = mox.Mox()
110 self.cnxn = 'fake connection'
111 self.project_service = fake.ProjectService()
112 self.config_service = fake.ConfigService()
113 self.cache_manager = fake.CacheManager()
114 self.issue_service = MakeIssueService(
115 self.project_service, self.config_service, self.cache_manager,
116 self.mox)
117 self.issue_2lc = self.issue_service.issue_2lc
118
119 now = int(time.time())
120 self.project_service.TestAddProject('proj', project_id=789)
121 self.issue_rows = [
122 (78901, 789, 1, 1, 111L, 222L, now, now, now, 0, 0, 0, 1, 0, False)]
123 self.summary_rows = [(78901, 'sum')]
124 self.label_rows = [(78901, 1, 0)]
125 self.component_rows = []
126 self.cc_rows = [(78901, 333L, 0)]
127 self.notify_rows = []
128 self.fieldvalue_rows = []
129 self.relation_rows = [
130 (78901, 78902, 'blockedon'), (78903, 78901, 'blockedon')]
131 self.dangling_relation_rows = [
132 (78901, 'codesite', 5001, 'blocking'),
133 (78901, 'codesite', 5002, 'blockedon')]
134
135 def tearDown(self):
136 self.mox.UnsetStubs()
137 self.mox.ResetAll()
138
139 def testDeserializeIssues_Empty(self):
140 issue_dict = self.issue_2lc._DeserializeIssues(
141 self.cnxn, [], [], [], [], [], [], [], [], [])
142 self.assertEqual({}, issue_dict)
143
144 def testDeserializeIssues_Normal(self):
145 issue_dict = self.issue_2lc._DeserializeIssues(
146 self.cnxn, self.issue_rows, self.summary_rows, self.label_rows,
147 self.component_rows, self.cc_rows, self.notify_rows,
148 self.fieldvalue_rows, self.relation_rows, self.dangling_relation_rows)
149 self.assertItemsEqual([78901], issue_dict.keys())
150
151 def testDeserializeIssues_UnexpectedLabel(self):
152 unexpected_label_rows = [
153 (78901, 999, 0)
154 ]
155 self.assertRaises(
156 AssertionError,
157 self.issue_2lc._DeserializeIssues,
158 self.cnxn, self.issue_rows, self.summary_rows, unexpected_label_rows,
159 self.component_rows, self.cc_rows, self.notify_rows,
160 self.fieldvalue_rows, self.relation_rows, self.dangling_relation_rows)
161
162 def testDeserializeIssues_UnexpectedIssueRelation(self):
163 unexpected_relation_rows = [
164 (78990, 78999, 'blockedon')
165 ]
166 self.assertRaises(
167 AssertionError,
168 self.issue_2lc._DeserializeIssues,
169 self.cnxn, self.issue_rows, self.summary_rows, self.label_rows,
170 self.component_rows, self.cc_rows, self.notify_rows,
171 self.fieldvalue_rows, unexpected_relation_rows,
172 self.dangling_relation_rows)
173
174 def SetUpFetchItems(self, issue_ids):
175 shard_id = None
176 self.issue_service.issue_tbl.Select(
177 self.cnxn, cols=issue_svc.ISSUE_COLS, id=issue_ids,
178 shard_id=shard_id).AndReturn(self.issue_rows)
179 self.issue_service.issuesummary_tbl.Select(
180 self.cnxn, cols=issue_svc.ISSUESUMMARY_COLS, shard_id=shard_id,
181 issue_id=issue_ids).AndReturn(self.summary_rows)
182 self.issue_service.issue2label_tbl.Select(
183 self.cnxn, cols=issue_svc.ISSUE2LABEL_COLS, shard_id=shard_id,
184 issue_id=issue_ids).AndReturn(self.label_rows)
185 self.issue_service.issue2component_tbl.Select(
186 self.cnxn, cols=issue_svc.ISSUE2COMPONENT_COLS, shard_id=shard_id,
187 issue_id=issue_ids).AndReturn(self.component_rows)
188 self.issue_service.issue2cc_tbl.Select(
189 self.cnxn, cols=issue_svc.ISSUE2CC_COLS, shard_id=shard_id,
190 issue_id=issue_ids).AndReturn(self.cc_rows)
191 self.issue_service.issue2notify_tbl.Select(
192 self.cnxn, cols=issue_svc.ISSUE2NOTIFY_COLS, shard_id=shard_id,
193 issue_id=issue_ids).AndReturn(self.notify_rows)
194 self.issue_service.issue2fieldvalue_tbl.Select(
195 self.cnxn, cols=issue_svc.ISSUE2FIELDVALUE_COLS, shard_id=shard_id,
196 issue_id=issue_ids).AndReturn(self.fieldvalue_rows)
197 self.issue_service.issuerelation_tbl.Select(
198 self.cnxn, cols=issue_svc.ISSUERELATION_COLS, # Note: no shard
199 where=[('(issue_id IN (%s) OR dst_issue_id IN (%s))',
200 issue_ids + issue_ids)]).AndReturn(self.relation_rows)
201 self.issue_service.danglingrelation_tbl.Select(
202 self.cnxn, cols=issue_svc.DANGLINGRELATION_COLS, # Note: no shard
203 issue_id=issue_ids).AndReturn(self.dangling_relation_rows)
204
205 def testFetchItems(self):
206 issue_ids = [78901]
207 self.SetUpFetchItems(issue_ids)
208 self.mox.ReplayAll()
209 issue_dict = self.issue_2lc.FetchItems(self.cnxn, issue_ids)
210 self.mox.VerifyAll()
211 self.assertItemsEqual(issue_ids, issue_dict.keys())
212
213
214 class IssueServiceTest(unittest.TestCase):
215
216 def setUp(self):
217 self.testbed = testbed.Testbed()
218 self.testbed.activate()
219 self.testbed.init_memcache_stub()
220
221 self.mox = mox.Mox()
222 self.cnxn = self.mox.CreateMock(sql.MonorailConnection)
223 self.services = service_manager.Services()
224 self.services.user = fake.UserService()
225 self.services.project = fake.ProjectService()
226 self.services.config = fake.ConfigService()
227 self.services.features = fake.FeaturesService()
228 self.cache_manager = fake.CacheManager()
229 self.services.issue = MakeIssueService(
230 self.services.project, self.services.config, self.cache_manager,
231 self.mox)
232 self.services.spam = self.mox.CreateMock(spam_svc.SpamService)
233 self.now = int(time.time())
234 self.orig_index_issues = tracker_fulltext.IndexIssues
235 tracker_fulltext.IndexIssues = lambda *args: None
236
237 def classifierResult(self, label, score):
238 return {'outputLabel': label,
239 'outputMulti': [{'label': label, 'score': score}]}
240
241 def tearDown(self):
242 self.testbed.deactivate()
243 self.mox.UnsetStubs()
244 self.mox.ResetAll()
245 tracker_fulltext.IndexIssues = self.orig_index_issues
246
247 ### Issue ID lookups
248
249 def testLookupIssueIDs_Hit(self):
250 self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901)
251 self.services.issue.issue_id_2lc.CacheItem((789, 2), 78902)
252 actual = self.services.issue.LookupIssueIDs(
253 self.cnxn, [(789, 1), (789, 2)])
254 self.assertEqual([78901, 78902], actual)
255
256 def testLookupIssueID(self):
257 self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901)
258 actual = self.services.issue.LookupIssueID(self.cnxn, 789, 1)
259 self.assertEqual(78901, actual)
260
261 def testResolveIssueRefs(self):
262 self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901)
263 self.services.issue.issue_id_2lc.CacheItem((789, 2), 78902)
264 prefetched_projects = {'proj': fake.Project('proj', project_id=789)}
265 refs = [('proj', 1), (None, 2)]
266 actual = self.services.issue.ResolveIssueRefs(
267 self.cnxn, prefetched_projects, 'proj', refs)
268 self.assertEqual([78901, 78902], actual)
269
270 ### Issue objects
271
272 def testCreateIssue(self):
273 settings.classifier_spam_thresh = 0.9
274 self.SetUpAllocateNextLocalID(789, None, None)
275 self.SetUpInsertIssue()
276 self.SetUpInsertComment(7890101, True)
277 self.services.spam.ClassifyIssue(mox.IgnoreArg(),
278 mox.IgnoreArg()).AndReturn(
279 self.classifierResult('ham', 1.0))
280 self.services.spam.RecordClassifierIssueVerdict(self.cnxn,
281 mox.IsA(tracker_pb2.Issue), False, 1.0)
282 self.SetUpUpdateIssuesModified(set())
283
284 self.mox.ReplayAll()
285 actual_local_id = self.services.issue.CreateIssue(
286 self.cnxn, self.services, 789, 'sum',
287 'New', 111L, [], ['Type-Defect'], [], [], 111L, 'content',
288 index_now=False, timestamp=self.now)
289 self.mox.VerifyAll()
290 self.assertEqual(1, actual_local_id)
291
292 def testCreateIssue_EmptyStringLabels(self):
293 settings.classifier_spam_thresh = 0.9
294 self.SetUpAllocateNextLocalID(789, None, None)
295 self.SetUpInsertIssue(label_rows=[])
296 self.SetUpInsertComment(7890101, True)
297 self.services.spam.ClassifyIssue(mox.IgnoreArg(),
298 mox.IgnoreArg()).AndReturn(
299 self.classifierResult('ham', 1.0))
300 self.services.spam.RecordClassifierIssueVerdict(self.cnxn,
301 mox.IsA(tracker_pb2.Issue), False, 1.0)
302 self.SetUpUpdateIssuesModified(set(), modified_timestamp=self.now)
303
304 self.mox.ReplayAll()
305 actual_local_id = self.services.issue.CreateIssue(
306 self.cnxn, self.services, 789, 'sum',
307 'New', 111L, [], [',', '', ' ', ', '], [], [], 111L, 'content',
308 index_now=False, timestamp=self.now)
309 self.mox.VerifyAll()
310 self.assertEqual(1, actual_local_id)
311
312 def SetUpUpdateIssuesModified(self, iids, modified_timestamp=None):
313 self.services.issue.issue_tbl.Update(
314 self.cnxn, {'modified': modified_timestamp or self.now},
315 id=iids, commit=False)
316
317 def testCreateIssue_spam(self):
318 settings.classifier_spam_thresh = 0.9
319 self.SetUpAllocateNextSpamID(789, None, None)
320 self.SetUpInsertSpamIssue()
321 self.SetUpInsertComment(7890101, True)
322
323 self.services.spam.ClassifyIssue(mox.IsA(tracker_pb2.Issue),
324 mox.IsA(tracker_pb2.IssueComment)).AndReturn(
325 self.classifierResult('spam', 1.0))
326 self.services.spam.RecordClassifierIssueVerdict(self.cnxn,
327 mox.IsA(tracker_pb2.Issue), True, 1.0)
328 self.SetUpUpdateIssuesModified(set())
329
330 self.mox.ReplayAll()
331 actual_local_id = self.services.issue.CreateIssue(
332 self.cnxn, self.services, 789, 'sum',
333 'New', 111L, [], ['Type-Defect'], [], [], 111L, 'content',
334 index_now=False, timestamp=self.now)
335 self.mox.VerifyAll()
336 self.assertEqual(-1, actual_local_id)
337
338 def testGetAllIssuesInProject_NoIssues(self):
339 self.SetUpGetHighestLocalID(789, None, None)
340 self.mox.ReplayAll()
341 issues = self.services.issue.GetAllIssuesInProject(self.cnxn, 789)
342 self.mox.VerifyAll()
343 self.assertEqual([], issues)
344
345 def testGetAnyOnHandIssue(self):
346 issue_ids = [78901, 78902, 78903]
347 self.SetUpGetIssues()
348 issue = self.services.issue.GetAnyOnHandIssue(issue_ids)
349 self.assertEqual(78901, issue.issue_id)
350
351 def SetUpGetIssues(self):
352 issue_1 = fake.MakeTestIssue(
353 project_id=789, local_id=1, owner_id=111L, summary='sum',
354 status='Live', issue_id=78901)
355 issue_1.project_name = 'proj'
356 issue_2 = fake.MakeTestIssue(
357 project_id=789, local_id=2, owner_id=111L, summary='sum',
358 status='Fixed', issue_id=78902)
359 issue_2.project_name = 'proj'
360 self.services.issue.issue_2lc.CacheItem(78901, issue_1)
361 self.services.issue.issue_2lc.CacheItem(78902, issue_2)
362 return issue_1, issue_2
363
364 def testGetIssuesDict(self):
365 issue_ids = [78901, 78902]
366 issue_1, issue_2 = self.SetUpGetIssues()
367 issues_dict = self.services.issue.GetIssuesDict(self.cnxn, issue_ids)
368 self.assertEqual(
369 {78901: issue_1, 78902: issue_2},
370 issues_dict)
371
372 def testGetIssues(self):
373 issue_ids = [78901, 78902]
374 issue_1, issue_2 = self.SetUpGetIssues()
375 issues = self.services.issue.GetIssues(self.cnxn, issue_ids)
376 self.assertEqual([issue_1, issue_2], issues)
377
378 def testGetIssue(self):
379 issue_1, _issue_2 = self.SetUpGetIssues()
380 actual_issue = self.services.issue.GetIssue(self.cnxn, 78901)
381 self.assertEqual(issue_1, actual_issue)
382
383 def testGetIssuesByLocalIDs(self):
384 issue_1, issue_2 = self.SetUpGetIssues()
385 self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901)
386 self.services.issue.issue_id_2lc.CacheItem((789, 2), 78902)
387 actual_issues = self.services.issue.GetIssuesByLocalIDs(
388 self.cnxn, 789, [1, 2])
389 self.assertEqual([issue_1, issue_2], actual_issues)
390
391 def testGetIssueByLocalID(self):
392 issue_1, _issue_2 = self.SetUpGetIssues()
393 self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901)
394 actual_issues = self.services.issue.GetIssueByLocalID(self.cnxn, 789, 1)
395 self.assertEqual(issue_1, actual_issues)
396
397 def testGetOpenAndClosedIssues(self):
398 issue_1, issue_2 = self.SetUpGetIssues()
399 open_issues, closed_issues = self.services.issue.GetOpenAndClosedIssues(
400 self.cnxn, [78901, 78902])
401 self.assertEqual([issue_1], open_issues)
402 self.assertEqual([issue_2], closed_issues)
403
404 def SetUpGetCurrentLocationOfMovedIssue(self, project_id, local_id):
405 issue_id = project_id * 100 + local_id
406 self.services.issue.issueformerlocations_tbl.SelectValue(
407 self.cnxn, 'issue_id', default=0, project_id=project_id,
408 local_id=local_id).AndReturn(issue_id)
409 self.services.issue.issue_tbl.SelectRow(
410 self.cnxn, cols=['project_id', 'local_id'], id=issue_id).AndReturn(
411 (project_id + 1, local_id + 1))
412
413 def testGetCurrentLocationOfMovedIssue(self):
414 self.SetUpGetCurrentLocationOfMovedIssue(789, 1)
415 self.mox.ReplayAll()
416 new_project_id, new_local_id = (
417 self.services.issue.GetCurrentLocationOfMovedIssue(self.cnxn, 789, 1))
418 self.mox.VerifyAll()
419 self.assertEqual(789 + 1, new_project_id)
420 self.assertEqual(1 + 1, new_local_id)
421
422 def SetUpGetPreviousLocations(self, issue_id, location_rows):
423 self.services.issue.issueformerlocations_tbl.Select(
424 self.cnxn, cols=['project_id', 'local_id'],
425 issue_id=issue_id).AndReturn(location_rows)
426
427 def testGetPreviousLocations(self):
428 self.SetUpGetPreviousLocations(78901, [(781, 1), (782, 11), (789, 1)])
429 self.mox.ReplayAll()
430 issue = fake.MakeTestIssue(
431 project_id=789, local_id=1, owner_id=111L, summary='sum',
432 status='Live', issue_id=78901)
433 locations = self.services.issue.GetPreviousLocations(self.cnxn, issue)
434 self.mox.VerifyAll()
435 self.assertEqual(locations, [(781, 1), (782, 11)])
436
437 def SetUpInsertIssue(self, label_rows=None):
438 row = (789, 1, 1, 111L, 111L, self.now, 0, self.now, None, 0,
439 False, 0, 0, False)
440 self.services.issue.issue_tbl.InsertRows(
441 self.cnxn, issue_svc.ISSUE_COLS[1:], [row],
442 commit=False, return_generated_ids=True).AndReturn([78901])
443 self.cnxn.Commit()
444 self.services.issue.issue_tbl.Update(
445 self.cnxn, {'shard': 78901 % settings.num_logical_shards},
446 id=78901, commit=False)
447 self.SetUpUpdateIssuesSummary()
448 self.SetUpUpdateIssuesLabels(label_rows=label_rows)
449 self.SetUpUpdateIssuesFields()
450 self.SetUpUpdateIssuesComponents()
451 self.SetUpUpdateIssuesCc()
452 self.SetUpUpdateIssuesNotify()
453 self.SetUpUpdateIssuesRelation()
454
455 def SetUpInsertSpamIssue(self):
456 row = (789, -1, 1, 111L, 111L, self.now, 0, self.now, None, 0,
457 False, 0, 0, True)
458 self.services.issue.issue_tbl.InsertRows(
459 self.cnxn, issue_svc.ISSUE_COLS[1:], [row],
460 commit=False, return_generated_ids=True).AndReturn([78901])
461 self.cnxn.Commit()
462 self.services.issue.issue_tbl.Update(
463 self.cnxn, {'shard': 78901 % settings.num_logical_shards},
464 id=78901, commit=False)
465 self.SetUpUpdateIssuesSummary()
466 self.SetUpUpdateIssuesLabels()
467 self.SetUpUpdateIssuesFields()
468 self.SetUpUpdateIssuesComponents()
469 self.SetUpUpdateIssuesCc()
470 self.SetUpUpdateIssuesNotify()
471 self.SetUpUpdateIssuesRelation()
472
473 def SetUpUpdateIssuesSummary(self):
474 self.services.issue.issuesummary_tbl.InsertRows(
475 self.cnxn, ['issue_id', 'summary'],
476 [(78901, 'sum')], replace=True, commit=False)
477
478 def SetUpUpdateIssuesLabels(self, label_rows=None):
479 if label_rows is None:
480 label_rows = [(78901, 1, False, 1)]
481 self.services.issue.issue2label_tbl.Delete(
482 self.cnxn, issue_id=[78901], commit=False)
483 self.services.issue.issue2label_tbl.InsertRows(
484 self.cnxn, ['issue_id', 'label_id', 'derived', 'issue_shard'],
485 label_rows, ignore=True, commit=False)
486
487 def SetUpUpdateIssuesFields(self, issue2fieldvalue_rows=None):
488 issue2fieldvalue_rows = issue2fieldvalue_rows or []
489 self.services.issue.issue2fieldvalue_tbl.Delete(
490 self.cnxn, issue_id=[78901], commit=False)
491 self.services.issue.issue2fieldvalue_tbl.InsertRows(
492 self.cnxn, issue_svc.ISSUE2FIELDVALUE_COLS + ['issue_shard'],
493 issue2fieldvalue_rows, commit=False)
494
495 def SetUpUpdateIssuesComponents(self, issue2component_rows=None):
496 issue2component_rows = issue2component_rows or []
497 self.services.issue.issue2component_tbl.Delete(
498 self.cnxn, issue_id=[78901], commit=False)
499 self.services.issue.issue2component_tbl.InsertRows(
500 self.cnxn, ['issue_id', 'component_id', 'derived', 'issue_shard'],
501 issue2component_rows, ignore=True, commit=False)
502
503 def SetUpUpdateIssuesCc(self, issue2cc_rows=None):
504 issue2cc_rows = issue2cc_rows or []
505 self.services.issue.issue2cc_tbl.Delete(
506 self.cnxn, issue_id=[78901], commit=False)
507 self.services.issue.issue2cc_tbl.InsertRows(
508 self.cnxn, ['issue_id', 'cc_id', 'derived', 'issue_shard'],
509 issue2cc_rows, ignore=True, commit=False)
510
511 def SetUpUpdateIssuesNotify(self, notify_rows=None):
512 notify_rows = notify_rows or []
513 self.services.issue.issue2notify_tbl.Delete(
514 self.cnxn, issue_id=[78901], commit=False)
515 self.services.issue.issue2notify_tbl.InsertRows(
516 self.cnxn, issue_svc.ISSUE2NOTIFY_COLS,
517 notify_rows, ignore=True, commit=False)
518
519 def SetUpUpdateIssuesRelation(
520 self, relation_rows=None, dangling_relation_rows=None):
521 relation_rows = relation_rows or []
522 dangling_relation_rows = dangling_relation_rows or []
523 self.services.issue.issuerelation_tbl.Delete(
524 self.cnxn, issue_id=[78901], commit=False)
525 self.services.issue.issuerelation_tbl.Delete(
526 self.cnxn, dst_issue_id=[78901], kind='blockedon',
527 commit=False)
528 self.services.issue.issuerelation_tbl.InsertRows(
529 self.cnxn, issue_svc.ISSUERELATION_COLS, relation_rows,
530 ignore=True, commit=False)
531 self.services.issue.danglingrelation_tbl.Delete(
532 self.cnxn, issue_id=[78901], commit=False)
533 self.services.issue.danglingrelation_tbl.InsertRows(
534 self.cnxn, issue_svc.DANGLINGRELATION_COLS, dangling_relation_rows,
535 ignore=True, commit=False)
536
537 def testInsertIssue(self):
538 self.SetUpInsertIssue()
539 self.mox.ReplayAll()
540 issue = fake.MakeTestIssue(
541 project_id=789, local_id=1, owner_id=111L, reporter_id=111L,
542 summary='sum', status='New', labels=['Type-Defect'], issue_id=78901,
543 opened_timestamp=self.now, modified_timestamp=self.now)
544 actual_issue_id = self.services.issue.InsertIssue(self.cnxn, issue)
545 self.mox.VerifyAll()
546 self.assertEqual(78901, actual_issue_id)
547
548 def SetUpUpdateIssues(self, given_delta=None):
549 delta = given_delta or {
550 'project_id': 789,
551 'local_id': 1,
552 'owner_id': 111L,
553 'status_id': 1,
554 'opened': 123456789,
555 'closed': 0,
556 'modified': 123456789,
557 'derived_owner_id': None,
558 'derived_status_id': None,
559 'deleted': False,
560 'star_count': 12,
561 'attachment_count': 0,
562 'is_spam': False,
563 }
564 self.services.issue.issue_tbl.Update(
565 self.cnxn, delta, id=78901, commit=False)
566 if not given_delta:
567 self.SetUpUpdateIssuesLabels()
568 self.SetUpUpdateIssuesCc()
569 self.SetUpUpdateIssuesFields()
570 self.SetUpUpdateIssuesComponents()
571 self.SetUpUpdateIssuesNotify()
572 self.SetUpUpdateIssuesSummary()
573 self.SetUpUpdateIssuesRelation()
574
575 self.cnxn.Commit()
576
577 def testUpdateIssues_Empty(self):
578 # Note: no setup because DB should not be called.
579 self.mox.ReplayAll()
580 self.services.issue.UpdateIssues(self.cnxn, [])
581 self.mox.VerifyAll()
582
583 def testUpdateIssues_Normal(self):
584 issue = fake.MakeTestIssue(
585 project_id=789, local_id=1, owner_id=111L, summary='sum',
586 status='Live', labels=['Type-Defect'], issue_id=78901,
587 opened_timestamp=123456789, modified_timestamp=123456789,
588 star_count=12)
589 self.SetUpUpdateIssues()
590 self.mox.ReplayAll()
591 self.services.issue.UpdateIssues(self.cnxn, [issue])
592 self.mox.VerifyAll()
593
594 def testUpdateIssue(self):
595 issue = fake.MakeTestIssue(
596 project_id=789, local_id=1, owner_id=111L, summary='sum',
597 status='Live', labels=['Type-Defect'], issue_id=78901,
598 opened_timestamp=123456789, modified_timestamp=123456789,
599 star_count=12)
600 self.SetUpUpdateIssues()
601 self.mox.ReplayAll()
602 self.services.issue.UpdateIssue(self.cnxn, issue)
603 self.mox.VerifyAll()
604
605 def testUpdateIssuesSummary(self):
606 issue = fake.MakeTestIssue(
607 local_id=1, issue_id=78901, owner_id=111L, summary='sum', status='New',
608 project_id=789)
609 self.SetUpUpdateIssuesSummary()
610 self.mox.ReplayAll()
611 self.services.issue._UpdateIssuesSummary(self.cnxn, [issue], commit=False)
612 self.mox.VerifyAll()
613
614 def testUpdateIssuesLabels(self):
615 issue = fake.MakeTestIssue(
616 local_id=1, issue_id=78901, owner_id=111L, summary='sum', status='New',
617 labels=['Type-Defect'], project_id=789)
618 self.SetUpUpdateIssuesLabels()
619 self.mox.ReplayAll()
620 self.services.issue._UpdateIssuesLabels(
621 self.cnxn, [issue], 789, commit=False)
622 self.mox.VerifyAll()
623
624 def testUpdateIssuesFields_Empty(self):
625 issue = fake.MakeTestIssue(
626 local_id=1, issue_id=78901, owner_id=111L, summary='sum', status='New',
627 project_id=789)
628 self.SetUpUpdateIssuesFields()
629 self.mox.ReplayAll()
630 self.services.issue._UpdateIssuesFields(self.cnxn, [issue], commit=False)
631 self.mox.VerifyAll()
632
633 def testUpdateIssuesFields_Some(self):
634 issue = fake.MakeTestIssue(
635 local_id=1, issue_id=78901, owner_id=111L, summary='sum', status='New',
636 project_id=789)
637 issue_shard = issue.issue_id % settings.num_logical_shards
638 fv1 = tracker_bizobj.MakeFieldValue(345, 679, '', 0L, False)
639 issue.field_values.append(fv1)
640 fv2 = tracker_bizobj.MakeFieldValue(346, 0, 'Blue', 0L, True)
641 issue.field_values.append(fv2)
642 self.SetUpUpdateIssuesFields(issue2fieldvalue_rows=[
643 (issue.issue_id, fv1.field_id, fv1.int_value, fv1.str_value,
644 None, fv1.derived, issue_shard),
645 (issue.issue_id, fv2.field_id, fv2.int_value, fv2.str_value,
646 None, fv2.derived, issue_shard),
647 ])
648 self.mox.ReplayAll()
649 self.services.issue._UpdateIssuesFields(self.cnxn, [issue], commit=False)
650 self.mox.VerifyAll()
651
652 def testUpdateIssuesComponents_Empty(self):
653 issue = fake.MakeTestIssue(
654 project_id=789, local_id=1, owner_id=111L, summary='sum',
655 status='Live', issue_id=78901)
656 self.SetUpUpdateIssuesComponents()
657 self.mox.ReplayAll()
658 self.services.issue._UpdateIssuesComponents(
659 self.cnxn, [issue], commit=False)
660 self.mox.VerifyAll()
661
662 def testUpdateIssuesCc_Empty(self):
663 issue = fake.MakeTestIssue(
664 project_id=789, local_id=1, owner_id=111L, summary='sum',
665 status='Live', issue_id=78901)
666 self.SetUpUpdateIssuesCc()
667 self.mox.ReplayAll()
668 self.services.issue._UpdateIssuesCc(self.cnxn, [issue], commit=False)
669 self.mox.VerifyAll()
670
671 def testUpdateIssuesCc_Some(self):
672 issue = fake.MakeTestIssue(
673 project_id=789, local_id=1, owner_id=111L, summary='sum',
674 status='Live', issue_id=78901)
675 issue.cc_ids = [222L, 333L]
676 issue.derived_cc_ids = [888L]
677 issue_shard = issue.issue_id % settings.num_logical_shards
678 self.SetUpUpdateIssuesCc(issue2cc_rows=[
679 (issue.issue_id, 222L, False, issue_shard),
680 (issue.issue_id, 333L, False, issue_shard),
681 (issue.issue_id, 888L, True, issue_shard),
682 ])
683 self.mox.ReplayAll()
684 self.services.issue._UpdateIssuesCc(self.cnxn, [issue], commit=False)
685 self.mox.VerifyAll()
686
687 def testUpdateIssuesNotify_Empty(self):
688 issue = fake.MakeTestIssue(
689 project_id=789, local_id=1, owner_id=111L, summary='sum',
690 status='Live', issue_id=78901)
691 self.SetUpUpdateIssuesNotify()
692 self.mox.ReplayAll()
693 self.services.issue._UpdateIssuesNotify(self.cnxn, [issue], commit=False)
694 self.mox.VerifyAll()
695
696 def testUpdateIssuesRelation_Empty(self):
697 issue = fake.MakeTestIssue(
698 project_id=789, local_id=1, owner_id=111L, summary='sum',
699 status='Live', issue_id=78901)
700 self.SetUpUpdateIssuesRelation()
701 self.mox.ReplayAll()
702 self.services.issue._UpdateIssuesRelation(self.cnxn, [issue], commit=False)
703 self.mox.VerifyAll()
704
705 def testDeltaUpdateIssue(self):
706 pass # TODO(jrobbins): write more tests
707
708 def testDeltaUpdateIssue_MergedInto(self):
709 commenter_id = 222L
710 issue = fake.MakeTestIssue(
711 project_id=789, local_id=1, owner_id=111L, summary='sum',
712 status='Live', issue_id=78901, project_name='proj')
713 target_issue = fake.MakeTestIssue(
714 project_id=789, local_id=2, owner_id=111L, summary='sum sum',
715 status='Live', issue_id=78902, project_name='proj')
716 config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
717
718 self.mox.StubOutWithMock(self.services.issue, 'GetIssue')
719 self.mox.StubOutWithMock(self.services.issue, 'UpdateIssue')
720 self.mox.StubOutWithMock(self.services.issue, 'CreateIssueComment')
721 self.mox.StubOutWithMock(self.services.issue, '_UpdateIssuesModified')
722
723 self.services.issue.GetIssue(
724 self.cnxn, 0).AndRaise(issue_svc.NoSuchIssueException)
725 self.services.issue.GetIssue(
726 self.cnxn, target_issue.issue_id).AndReturn(target_issue)
727 self.services.issue.UpdateIssue(
728 self.cnxn, issue, commit=False, invalidate=False)
729 amendments = [
730 tracker_bizobj.MakeMergedIntoAmendment(
731 ('proj', 2), None, default_project_name='proj')]
732 self.services.issue.CreateIssueComment(
733 self.cnxn, 789, 1, commenter_id, 'comment text',
734 amendments=amendments, commit=False)
735 self.services.issue._UpdateIssuesModified(
736 self.cnxn, {issue.issue_id, target_issue.issue_id},
737 modified_timestamp=self.now, invalidate=True)
738
739 self.mox.ReplayAll()
740 self.services.issue.DeltaUpdateIssue(
741 self.cnxn, self.services, commenter_id, issue.project_id, config,
742 issue, issue.status, issue.owner_id,
743 [], [], [], [], [], [], [], [], [],
744 merged_into=target_issue.issue_id, comment='comment text',
745 index_now=False, timestamp=self.now)
746 self.mox.VerifyAll()
747
748 def testApplyIssueComment(self):
749 issue = fake.MakeTestIssue(
750 project_id=789, local_id=1, owner_id=111L, summary='sum',
751 status='Live', issue_id=78901)
752
753 self.mox.StubOutWithMock(self.services.issue, 'GetIssueByLocalID')
754 self.mox.StubOutWithMock(self.services.issue, 'UpdateIssues')
755 self.mox.StubOutWithMock(self.services.issue, 'GetCommentsForIssue')
756 self.mox.StubOutWithMock(self.services.issue, 'SoftDeleteComment')
757 self.mox.StubOutWithMock(self.services.issue, "CreateIssueComment")
758 self.mox.StubOutWithMock(self.services.issue, "_UpdateIssuesModified")
759
760 self.services.issue.GetIssueByLocalID(self.cnxn, issue.project_id,
761 issue.local_id).AndReturn(issue)
762 self.services.issue.CreateIssueComment(self.cnxn, issue.project_id,
763 issue.local_id, issue.reporter_id, 'comment text',
764 amendments=[], attachments=None, inbound_message=None,
765 is_spam=False)
766 self.services.issue.UpdateIssues(self.cnxn, [issue],
767 just_derived=False, update_cols=None, commit=True, invalidate=True)
768 self.services.spam.ClassifyComment('comment text').AndReturn(
769 self.classifierResult('ham', 1.0))
770 self.services.spam.RecordClassifierCommentVerdict(self.cnxn,
771 None, False, 1.0)
772 self.services.issue._UpdateIssuesModified(
773 self.cnxn, set(), modified_timestamp=self.now)
774
775 self.mox.ReplayAll()
776 self.services.issue.ApplyIssueComment(self.cnxn, self.services,
777 issue.reporter_id, issue.project_id, issue.local_id, issue.summary,
778 issue.status, issue.owner_id, issue.cc_ids, issue.labels,
779 issue.field_values, issue.component_ids, [],
780 [], [], [], issue.merged_into, comment='comment text',
781 timestamp=self.now)
782 self.mox.VerifyAll()
783
784 def testApplyIssueComment_spam(self):
785 settings.classifier_spam_thresh = 0.5
786
787 issue = fake.MakeTestIssue(
788 project_id=789, local_id=1, owner_id=111L, summary='sum',
789 status='Live', issue_id=78901)
790
791 self.mox.StubOutWithMock(self.services.issue, "GetIssueByLocalID")
792 self.mox.StubOutWithMock(self.services.issue, "UpdateIssues")
793 self.mox.StubOutWithMock(self.services.issue, "GetCommentsForIssue")
794 self.mox.StubOutWithMock(self.services.issue, "CreateIssueComment")
795 self.mox.StubOutWithMock(self.services.issue, "SoftDeleteComment")
796 self.mox.StubOutWithMock(self.services.issue, "_UpdateIssuesModified")
797
798 self.services.issue.GetIssueByLocalID(self.cnxn, 789, 1).AndReturn(issue)
799 self.services.issue.UpdateIssues(self.cnxn, [issue],
800 just_derived=False, update_cols=None, commit=True, invalidate=True)
801 self.services.issue.GetCommentsForIssue(self.cnxn,
802 issue.issue_id).AndReturn([""])
803 self.services.issue.SoftDeleteComment(self.cnxn,
804 issue.project_id, issue.local_id, 0, issue.reporter_id,
805 self.services.user, is_spam=True)
806 self.services.spam.ClassifyComment('comment text').AndReturn(
807 self.classifierResult('spam', 1.0))
808 self.services.spam.RecordClassifierCommentVerdict(self.cnxn,
809 mox.IsA(tracker_pb2.IssueComment), True, 1.0)
810 self.services.issue.CreateIssueComment(self.cnxn, issue.project_id,
811 issue.local_id, issue.reporter_id, 'comment text',
812 amendments=[], attachments=None, inbound_message=None,
813 is_spam=True).AndReturn(tracker_pb2.IssueComment())
814 self.services.issue._UpdateIssuesModified(
815 self.cnxn, set(), modified_timestamp=self.now)
816
817 self.mox.ReplayAll()
818 self.services.issue.ApplyIssueComment(self.cnxn, self.services,
819 issue.reporter_id, issue.project_id, issue.local_id, issue.summary,
820 issue.status, issue.owner_id, issue.cc_ids, issue.labels,
821 issue.field_values, issue.component_ids, [],
822 [], [], [], issue.merged_into, comment='comment text',
823 timestamp=self.now)
824 self.mox.VerifyAll()
825
826 def testApplyIssueComment_blockedon(self):
827 issue = fake.MakeTestIssue(
828 project_id=789, local_id=1, owner_id=111L, summary='sum',
829 status='Live', issue_id=78901)
830 blockedon_issue = fake.MakeTestIssue(
831 project_id=789, local_id=2, owner_id=111L, summary='sum',
832 status='Live', issue_id=78902)
833
834 self.mox.StubOutWithMock(self.services.issue, "GetIssueByLocalID")
835 self.mox.StubOutWithMock(self.services.issue, "UpdateIssues")
836 self.mox.StubOutWithMock(self.services.issue, "CreateIssueComment")
837 self.mox.StubOutWithMock(self.services.issue, "GetIssues")
838 self.mox.StubOutWithMock(self.services.issue, "_UpdateIssuesModified")
839 # Call to find added blockedon issues.
840 self.services.issue.GetIssues(
841 self.cnxn, [blockedon_issue.issue_id]).AndReturn([blockedon_issue])
842 # Call to find removed blockedon issues.
843 self.services.issue.GetIssues(self.cnxn, []).AndReturn([])
844
845 self.services.issue.GetIssueByLocalID(self.cnxn, 789, 1).AndReturn(issue)
846 self.services.issue.UpdateIssues(self.cnxn, [issue],
847 just_derived=False, update_cols=None, commit=True, invalidate=True)
848 self.services.spam.ClassifyComment('comment text').AndReturn(
849 self.classifierResult('ham', 1.0))
850 self.services.spam.RecordClassifierCommentVerdict(self.cnxn,
851 mox.IsA(tracker_pb2.IssueComment), False, 1.0)
852 self.services.issue.CreateIssueComment(self.cnxn, issue.project_id,
853 issue.local_id, issue.reporter_id, 'comment text',
854 amendments=[
855 tracker_bizobj.MakeBlockedOnAmendment(
856 [(blockedon_issue.project_name, blockedon_issue.local_id)], [],
857 default_project_name=blockedon_issue.project_name)],
858 attachments=None, inbound_message=None,
859 is_spam=False).AndReturn(tracker_pb2.IssueComment())
860 # Add a comment on the blockedon issue.
861 self.services.issue.CreateIssueComment(
862 self.cnxn, blockedon_issue.project_id, blockedon_issue.local_id,
863 blockedon_issue.reporter_id, content='',
864 amendments=[tracker_bizobj.MakeBlockingAmendment(
865 [(issue.project_name, issue.local_id)], [],
866 default_project_name=issue.project_name)])
867 self.services.issue._UpdateIssuesModified(
868 self.cnxn, {blockedon_issue.issue_id}, modified_timestamp=self.now)
869
870 self.mox.ReplayAll()
871 self.services.issue.ApplyIssueComment(self.cnxn, self.services,
872 issue.reporter_id, issue.project_id, issue.local_id, issue.summary,
873 issue.status, issue.owner_id, issue.cc_ids, issue.labels,
874 issue.field_values, issue.component_ids, [blockedon_issue.issue_id],
875 [], [], [], issue.merged_into, comment='comment text',
876 timestamp=self.now)
877 self.mox.VerifyAll()
878
879 def SetUpMoveIssues_NewProject(self):
880 self.services.issue.issueformerlocations_tbl.Select(
881 self.cnxn, cols=issue_svc.ISSUEFORMERLOCATIONS_COLS, project_id=789,
882 issue_id=[78901]).AndReturn([])
883 self.SetUpAllocateNextLocalID(789, None, None)
884 self.SetUpUpdateIssues()
885 self.services.issue.comment_tbl.Update(
886 self.cnxn, {'project_id': 789}, issue_id=[78901], commit=False)
887
888 old_location_rows = [(78901, 711, 2)]
889 self.services.issue.issueformerlocations_tbl.InsertRows(
890 self.cnxn, issue_svc.ISSUEFORMERLOCATIONS_COLS, old_location_rows,
891 ignore=True, commit=False)
892 self.cnxn.Commit()
893
894 def testMoveIssues_NewProject(self):
895 """Move project 711 issue 2 to become project 789 issue 1."""
896 dest_project = fake.Project(project_id=789)
897 issue = fake.MakeTestIssue(
898 project_id=711, local_id=2, owner_id=111L, summary='sum',
899 status='Live', labels=['Type-Defect'], issue_id=78901,
900 opened_timestamp=123456789, modified_timestamp=123456789,
901 star_count=12)
902 self.SetUpMoveIssues_NewProject()
903 self.mox.ReplayAll()
904 self.services.issue.MoveIssues(
905 self.cnxn, dest_project, [issue], self.services.user)
906 self.mox.VerifyAll()
907
908 # TODO(jrobbins): case where issue is moved back into former project
909
910 def testExpungeFormerLocations(self):
911 self.services.issue.issueformerlocations_tbl.Delete(
912 self.cnxn, project_id=789)
913
914 self.mox.ReplayAll()
915 self.services.issue.ExpungeFormerLocations(self.cnxn, 789)
916 self.mox.VerifyAll()
917
918 def testExpungeIssues(self):
919 issue_ids = [1, 2]
920
921 self.mox.StubOutWithMock(search, 'Index')
922 search.Index(name=settings.search_index_name_format % 1).AndReturn(
923 MockIndex())
924 search.Index(name=settings.search_index_name_format % 2).AndReturn(
925 MockIndex())
926
927 self.services.issue.issuesummary_tbl.Delete(self.cnxn, issue_id=[1, 2])
928 self.services.issue.issue2label_tbl.Delete(self.cnxn, issue_id=[1, 2])
929 self.services.issue.issue2component_tbl.Delete(self.cnxn, issue_id=[1, 2])
930 self.services.issue.issue2cc_tbl.Delete(self.cnxn, issue_id=[1, 2])
931 self.services.issue.issue2notify_tbl.Delete(self.cnxn, issue_id=[1, 2])
932 self.services.issue.issueupdate_tbl.Delete(self.cnxn, issue_id=[1, 2])
933 self.services.issue.attachment_tbl.Delete(self.cnxn, issue_id=[1, 2])
934 self.services.issue.comment_tbl.Delete(self.cnxn, issue_id=[1, 2])
935 self.services.issue.issuerelation_tbl.Delete(self.cnxn, issue_id=[1, 2])
936 self.services.issue.issuerelation_tbl.Delete(self.cnxn, dst_issue_id=[1, 2])
937 self.services.issue.danglingrelation_tbl.Delete(self.cnxn, issue_id=[1, 2])
938 self.services.issue.issueformerlocations_tbl.Delete(
939 self.cnxn, issue_id=[1, 2])
940 self.services.issue.reindexqueue_tbl.Delete(self.cnxn, issue_id=[1, 2])
941 self.services.issue.issue_tbl.Delete(self.cnxn, id=[1, 2])
942
943 self.mox.ReplayAll()
944 self.services.issue.ExpungeIssues(self.cnxn, issue_ids)
945 self.mox.VerifyAll()
946
947 def testSoftDeleteIssue(self):
948 project = fake.Project(project_id=789)
949 issue_1, _issue_2 = self.SetUpGetIssues()
950 self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901)
951 delta = {'deleted': True}
952 self.services.issue.issue_tbl.Update(
953 self.cnxn, delta, id=78901, commit=False)
954 self.cnxn.Commit()
955 self.mox.ReplayAll()
956 self.services.issue.SoftDeleteIssue(
957 self.cnxn, project.project_id, 1, True, self.services.user)
958 self.mox.VerifyAll()
959 self.assertTrue(issue_1.deleted)
960
961 def SetUpDeleteComponentReferences(self, component_id):
962 self.services.issue.issue2component_tbl.Delete(
963 self.cnxn, component_id=component_id)
964
965 def testDeleteComponentReferences(self):
966 self.SetUpDeleteComponentReferences(123)
967 self.mox.ReplayAll()
968 self.services.issue.DeleteComponentReferences(self.cnxn, 123)
969 self.mox.VerifyAll()
970
971 ### Local ID generation
972
973 def SetUpInitializeLocalID(self, project_id):
974 self.services.issue.localidcounter_tbl.InsertRow(
975 self.cnxn, project_id=project_id, used_local_id=0, used_spam_id=0)
976
977 def testInitializeLocalID(self):
978 self.SetUpInitializeLocalID(789)
979 self.mox.ReplayAll()
980 self.services.issue.InitializeLocalID(self.cnxn, 789)
981 self.mox.VerifyAll()
982
983 def SetUpAllocateNextLocalID(
984 self, project_id, highest_in_use, highest_former):
985 highest_either = max(highest_in_use or 0, highest_former or 0)
986 self.services.issue.localidcounter_tbl.IncrementCounterValue(
987 self.cnxn, 'used_local_id', project_id=project_id).AndReturn(
988 highest_either + 1)
989
990 def SetUpAllocateNextSpamID(
991 self, project_id, highest_in_use, highest_former):
992 highest_either = max(highest_in_use or 0, highest_former or 0)
993 self.services.issue.localidcounter_tbl.IncrementCounterValue(
994 self.cnxn, 'used_spam_id', project_id=project_id).AndReturn(
995 highest_either + 1)
996
997 def testAllocateNextLocalID_NewProject(self):
998 self.SetUpAllocateNextLocalID(789, None, None)
999 self.mox.ReplayAll()
1000 next_local_id = self.services.issue.AllocateNextLocalID(self.cnxn, 789)
1001 self.mox.VerifyAll()
1002 self.assertEqual(1, next_local_id)
1003
1004 def testAllocateNextLocalID_HighestInUse(self):
1005 self.SetUpAllocateNextLocalID(789, 14, None)
1006 self.mox.ReplayAll()
1007 next_local_id = self.services.issue.AllocateNextLocalID(self.cnxn, 789)
1008 self.mox.VerifyAll()
1009 self.assertEqual(15, next_local_id)
1010
1011 def testAllocateNextLocalID_HighestWasMoved(self):
1012 self.SetUpAllocateNextLocalID(789, 23, 66)
1013 self.mox.ReplayAll()
1014 next_local_id = self.services.issue.AllocateNextLocalID(self.cnxn, 789)
1015 self.mox.VerifyAll()
1016 self.assertEqual(67, next_local_id)
1017
1018 def SetUpGetHighestLocalID(self, project_id, highest_in_use, highest_former):
1019 self.services.issue.issue_tbl.SelectValue(
1020 self.cnxn, 'MAX(local_id)', project_id=project_id).AndReturn(
1021 highest_in_use)
1022 self.services.issue.issueformerlocations_tbl.SelectValue(
1023 self.cnxn, 'MAX(local_id)', project_id=project_id).AndReturn(
1024 highest_former)
1025
1026 def testGetHighestLocalID_OnlyActiveLocalIDs(self):
1027 self.SetUpGetHighestLocalID(789, 14, None)
1028 self.mox.ReplayAll()
1029 highest_id = self.services.issue.GetHighestLocalID(self.cnxn, 789)
1030 self.mox.VerifyAll()
1031 self.assertEqual(14, highest_id)
1032
1033 def testGetHighestLocalID_OnlyFormerIDs(self):
1034 self.SetUpGetHighestLocalID(789, None, 97)
1035 self.mox.ReplayAll()
1036 highest_id = self.services.issue.GetHighestLocalID(self.cnxn, 789)
1037 self.mox.VerifyAll()
1038 self.assertEqual(97, highest_id)
1039
1040 def testGetHighestLocalID_BothActiveAndFormer(self):
1041 self.SetUpGetHighestLocalID(789, 345, 97)
1042 self.mox.ReplayAll()
1043 highest_id = self.services.issue.GetHighestLocalID(self.cnxn, 789)
1044 self.mox.VerifyAll()
1045 self.assertEqual(345, highest_id)
1046
1047 def testGetAllLocalIDsInProject(self):
1048 self.SetUpGetHighestLocalID(789, 14, None)
1049 self.mox.ReplayAll()
1050 local_id_range = self.services.issue.GetAllLocalIDsInProject(self.cnxn, 789)
1051 self.mox.VerifyAll()
1052 self.assertEqual(range(1, 15), local_id_range)
1053
1054 ### Comments
1055
1056 def testDeserializeComments_Empty(self):
1057 comments = self.services.issue._DeserializeComments([], [], [])
1058 self.assertEqual([], comments)
1059
1060 def SetUpCommentRows(self):
1061 comment_rows = [
1062 (7890101, 78901, self.now, 789, 111L,
1063 'content', None, True, None, False)]
1064 amendment_rows = [
1065 (1, 78901, 7890101, 'cc', 'old', 'new val', 222, None, None)]
1066 attachment_rows = []
1067 return comment_rows, amendment_rows, attachment_rows
1068
1069 def testDeserializeComments(self):
1070 comment_rows, amendment_rows, attachment_rows = self.SetUpCommentRows()
1071 comments = self.services.issue._DeserializeComments(
1072 comment_rows, amendment_rows, attachment_rows)
1073 self.assertEqual(1, len(comments))
1074
1075 def SetUpGetComments(self, issue_ids):
1076 # Assumes one comment per issue.
1077 cids = [issue_id + 1000 for issue_id in issue_ids]
1078 self.services.issue.comment_tbl.Select(
1079 self.cnxn, cols=['Comment.id'] + issue_svc.COMMENT_COLS[1:],
1080 where=None, issue_id=issue_ids, order_by=[('created', [])]).AndReturn([
1081 (issue_id + 1000, issue_id, self.now, 789, 111L, 'content',
1082 None, True, None, False) for issue_id in issue_ids])
1083 # Assume no amendments or attachment for now.
1084 self.services.issue.issueupdate_tbl.Select(
1085 self.cnxn, cols=issue_svc.ISSUEUPDATE_COLS,
1086 comment_id=cids).AndReturn([])
1087 if issue_ids:
1088 attachment_rows = [
1089 (1234, issue_ids[0], cids[0], 'a_filename', 1024, 'text/plain',
1090 False, None)]
1091 else:
1092 attachment_rows = []
1093
1094 self.services.issue.attachment_tbl.Select(
1095 self.cnxn, cols=issue_svc.ATTACHMENT_COLS,
1096 comment_id=cids).AndReturn(attachment_rows)
1097
1098 def testGetComments(self):
1099 self.SetUpGetComments([100001, 100002])
1100 self.mox.ReplayAll()
1101 comments = self.services.issue.GetComments(
1102 self.cnxn, issue_id=[100001, 100002])
1103 self.mox.VerifyAll()
1104 self.assertEqual(2, len(comments))
1105 self.assertEqual('content', comments[0].content)
1106 self.assertEqual('content', comments[1].content)
1107
1108 def SetUpGetComment_Found(self, comment_id):
1109 # Assumes one comment per issue.
1110 self.services.issue.comment_tbl.Select(
1111 self.cnxn, cols=['Comment.id'] + issue_svc.COMMENT_COLS[1:],
1112 where=None, id=comment_id, order_by=[('created', [])]).AndReturn([
1113 (comment_id, int(comment_id / 100), self.now, 789, 111L, 'content',
1114 None, True, None, False)])
1115 # Assume no amendments or attachment for now.
1116 self.services.issue.issueupdate_tbl.Select(
1117 self.cnxn, cols=issue_svc.ISSUEUPDATE_COLS,
1118 comment_id=[comment_id]).AndReturn([])
1119 self.services.issue.attachment_tbl.Select(
1120 self.cnxn, cols=issue_svc.ATTACHMENT_COLS,
1121 comment_id=[comment_id]).AndReturn([])
1122
1123 def testGetComment_Found(self):
1124 self.SetUpGetComment_Found(7890101)
1125 self.mox.ReplayAll()
1126 comment = self.services.issue.GetComment(self.cnxn, 7890101)
1127 self.mox.VerifyAll()
1128 self.assertEqual('content', comment.content)
1129
1130 def SetUpGetComment_Missing(self, comment_id):
1131 # Assumes one comment per issue.
1132 self.services.issue.comment_tbl.Select(
1133 self.cnxn, cols=['Comment.id'] + issue_svc.COMMENT_COLS[1:],
1134 where=None, id=comment_id, order_by=[('created', [])]).AndReturn([])
1135 # Assume no amendments or attachment for now.
1136 self.services.issue.issueupdate_tbl.Select(
1137 self.cnxn, cols=issue_svc.ISSUEUPDATE_COLS,
1138 comment_id=[]).AndReturn([])
1139 self.services.issue.attachment_tbl.Select(
1140 self.cnxn, cols=issue_svc.ATTACHMENT_COLS, comment_id=[]).AndReturn([])
1141
1142 def testGetComment_Missing(self):
1143 self.SetUpGetComment_Missing(7890101)
1144 self.mox.ReplayAll()
1145 self.assertRaises(
1146 issue_svc.NoSuchCommentException,
1147 self.services.issue.GetComment, self.cnxn, 7890101)
1148 self.mox.VerifyAll()
1149
1150 def testGetCommentsForIssue(self):
1151 issue = fake.MakeTestIssue(789, 1, 'Summary', 'New', 111L)
1152 self.SetUpGetComments([issue.issue_id])
1153 self.mox.ReplayAll()
1154 self.services.issue.GetCommentsForIssue(self.cnxn, issue.issue_id)
1155 self.mox.VerifyAll()
1156
1157 def testGetCommentsForIssues(self):
1158 self.SetUpGetComments([100001, 100002])
1159 self.mox.ReplayAll()
1160 self.services.issue.GetCommentsForIssues(
1161 self.cnxn, issue_ids=[100001, 100002])
1162 self.mox.VerifyAll()
1163
1164 def SetUpInsertComment(self, comment_id, was_escaped, is_spam=False):
1165 self.services.issue.comment_tbl.InsertRow(
1166 self.cnxn, issue_id=78901, created=self.now, project_id=789,
1167 commenter_id=111L, content='content', inbound_message=None,
1168 was_escaped=was_escaped, deleted_by=None, is_spam=is_spam,
1169 commit=True).AndReturn(comment_id)
1170
1171 amendment_rows = []
1172 self.services.issue.issueupdate_tbl.InsertRows(
1173 self.cnxn, issue_svc.ISSUEUPDATE_COLS[1:], amendment_rows,
1174 commit=True)
1175
1176 attachment_rows = []
1177 self.services.issue.attachment_tbl.InsertRows(
1178 self.cnxn, issue_svc.ATTACHMENT_COLS[1:], attachment_rows,
1179 commit=True)
1180
1181 def testInsertComment(self):
1182 self.SetUpInsertComment(7890101, False)
1183 self.mox.ReplayAll()
1184 comment = tracker_pb2.IssueComment(
1185 issue_id=78901, timestamp=self.now, project_id=789, user_id=111L,
1186 content='content', was_escaped=False)
1187 self.services.issue.InsertComment(self.cnxn, comment, commit=True)
1188 self.mox.VerifyAll()
1189 self.assertEqual(7890101, comment.id)
1190
1191 def SetUpUpdateComment(self, comment_id, delta=None):
1192 delta = delta or {
1193 'commenter_id': 111L,
1194 'content': 'new content',
1195 'deleted_by': 222L,
1196 'is_spam': False,
1197 }
1198 self.services.issue.comment_tbl.Update(
1199 self.cnxn, delta, id=comment_id)
1200
1201 def testUpdateComment(self):
1202 self.SetUpUpdateComment(7890101)
1203 self.mox.ReplayAll()
1204 comment = tracker_pb2.IssueComment(
1205 id=7890101, issue_id=78901, timestamp=self.now, project_id=789,
1206 user_id=111L, content='new content', was_escaped=True, deleted_by=222L,
1207 is_spam=False)
1208 self.services.issue._UpdateComment(self.cnxn, comment)
1209 self.mox.VerifyAll()
1210
1211 def testMakeIssueComment(self):
1212 comment = self.services.issue._MakeIssueComment(
1213 789, 111L, 'content', timestamp=self.now)
1214 self.assertEqual('content', comment.content)
1215 self.assertEqual([], comment.amendments)
1216 self.assertEqual([], comment.attachments)
1217
1218 def testCreateIssueComment(self):
1219 _issue_1, _issue_2 = self.SetUpGetIssues()
1220 self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901)
1221 self.SetUpInsertComment(7890101, False)
1222 self.mox.ReplayAll()
1223 comment = self.services.issue.CreateIssueComment(
1224 self.cnxn, 789, 1, 111L, 'content', timestamp=self.now)
1225 self.mox.VerifyAll()
1226 self.assertEqual('content', comment.content)
1227
1228 def testCreateIssueComment_spam(self):
1229 _issue_1, _issue_2 = self.SetUpGetIssues()
1230 self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901)
1231 self.SetUpInsertComment(7890101, False, is_spam=True)
1232 self.mox.ReplayAll()
1233 comment = self.services.issue.CreateIssueComment(
1234 self.cnxn, 789, 1, 111L, 'content', timestamp=self.now, is_spam=True)
1235 self.mox.VerifyAll()
1236 self.assertEqual('content', comment.content)
1237 self.assertTrue(comment.is_spam)
1238
1239 def testSoftDeleteComment(self):
1240 issue_1, _issue_2 = self.SetUpGetIssues()
1241 issue_1.attachment_count = 1
1242 self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901)
1243 self.SetUpGetComments([78901])
1244 self.SetUpUpdateComment(79901, delta={'deleted_by': 222L, 'is_spam': False})
1245 self.SetUpUpdateIssues(given_delta={'attachment_count': 0})
1246 self.mox.ReplayAll()
1247 self.services.issue.SoftDeleteComment(
1248 self.cnxn, 789, 1, 0, 222L, self.services.user)
1249 self.mox.VerifyAll()
1250
1251 ### Attachments
1252
1253 def testGetAttachmentAndContext(self):
1254 # TODO(jrobbins): re-implemnent to use Google Cloud Storage.
1255 pass
1256
1257 def SetUpUpdateAttachment(self, attachment_id, delta):
1258 self.services.issue.attachment_tbl.Update(
1259 self.cnxn, delta, id=attachment_id)
1260
1261 def testUpdateAttachment(self):
1262 delta = {
1263 'filename': 'a_filename',
1264 'filesize': 1024,
1265 'mimetype': 'text/plain',
1266 'deleted': False,
1267 }
1268 self.SetUpUpdateAttachment(1234, delta)
1269 self.mox.ReplayAll()
1270 attach = tracker_pb2.Attachment(
1271 attachment_id=1234, filename='a_filename', filesize=1024,
1272 mimetype='text/plain')
1273 self.services.issue._UpdateAttachment(self.cnxn, attach)
1274 self.mox.VerifyAll()
1275
1276 def testStoreAttachmentBlob(self):
1277 # TODO(jrobbins): re-implemnent to use Google Cloud Storage.
1278 pass
1279
1280 def testSoftDeleteAttachment(self):
1281 issue_1, _issue_2 = self.SetUpGetIssues()
1282 issue_1.attachment_count = 1
1283 self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901)
1284 self.SetUpGetComments([78901])
1285 self.SetUpUpdateAttachment(1234, {'deleted': True})
1286 self.SetUpUpdateIssues(given_delta={'attachment_count': 0})
1287
1288 self.mox.ReplayAll()
1289 self.services.issue.SoftDeleteAttachment(
1290 self.cnxn, 789, 1, 0, 1234, self.services.user)
1291 self.mox.VerifyAll()
1292
1293 ### Reindex queue
1294
1295 def SetUpEnqueueIssuesForIndexing(self, issue_ids):
1296 reindex_rows = [(issue_id,) for issue_id in issue_ids]
1297 self.services.issue.reindexqueue_tbl.InsertRows(
1298 self.cnxn, ['issue_id'], reindex_rows, ignore=True)
1299
1300 def testEnqueueIssuesForIndexing(self):
1301 self.SetUpEnqueueIssuesForIndexing([78901])
1302 self.mox.ReplayAll()
1303 self.services.issue.EnqueueIssuesForIndexing(self.cnxn, [78901])
1304 self.mox.VerifyAll()
1305
1306 def SetUpReindexIssues(self, issue_ids):
1307 self.services.issue.reindexqueue_tbl.Select(
1308 self.cnxn, order_by=[('created', [])],
1309 limit=50).AndReturn([(issue_id,) for issue_id in issue_ids])
1310
1311 if issue_ids:
1312 _issue_1, _issue_2 = self.SetUpGetIssues()
1313 self.services.issue.reindexqueue_tbl.Delete(
1314 self.cnxn, issue_id=issue_ids)
1315
1316 def testReindexIssues_QueueEmpty(self):
1317 self.SetUpReindexIssues([])
1318 self.mox.ReplayAll()
1319 self.services.issue.ReindexIssues(self.cnxn, 50, self.services.user)
1320 self.mox.VerifyAll()
1321
1322 def testReindexIssues_QueueHasTwoIssues(self):
1323 self.SetUpReindexIssues([78901, 78902])
1324 self.mox.ReplayAll()
1325 self.services.issue.ReindexIssues(self.cnxn, 50, self.services.user)
1326 self.mox.VerifyAll()
1327
1328 ### Search functions
1329
1330 def SetUpRunIssueQuery(
1331 self, rows, limit=settings.search_limit_per_shard):
1332 self.services.issue.issue_tbl.Select(
1333 self.cnxn, shard_id=1, distinct=True, cols=['Issue.id'],
1334 left_joins=[], where=[('Issue.deleted = %s', [False])], order_by=[],
1335 limit=limit).AndReturn(rows)
1336
1337 def testRunIssueQuery_NoResults(self):
1338 self.SetUpRunIssueQuery([])
1339 self.mox.ReplayAll()
1340 result_iids, capped = self.services.issue.RunIssueQuery(
1341 self.cnxn, [], [], [], shard_id=1)
1342 self.mox.VerifyAll()
1343 self.assertEqual([], result_iids)
1344 self.assertFalse(capped)
1345
1346 def testRunIssueQuery_Normal(self):
1347 self.SetUpRunIssueQuery([(1,), (11,), (21,)])
1348 self.mox.ReplayAll()
1349 result_iids, capped = self.services.issue.RunIssueQuery(
1350 self.cnxn, [], [], [], shard_id=1)
1351 self.mox.VerifyAll()
1352 self.assertEqual([1, 11, 21], result_iids)
1353 self.assertFalse(capped)
1354
1355 def testRunIssueQuery_Capped(self):
1356 try:
1357 orig = settings.search_limit_per_shard
1358 settings.search_limit_per_shard = 3
1359 self.SetUpRunIssueQuery([(1,), (11,), (21,)], limit=3)
1360 self.mox.ReplayAll()
1361 result_iids, capped = self.services.issue.RunIssueQuery(
1362 self.cnxn, [], [], [], shard_id=1)
1363 self.mox.VerifyAll()
1364 self.assertEqual([1, 11, 21], result_iids)
1365 self.assertTrue(capped)
1366 finally:
1367 settings.search_limit_per_shard = orig
1368
1369 def SetUpGetIIDsByLabelIDs(self):
1370 self.services.issue.issue_tbl.Select(
1371 self.cnxn, shard_id=1, cols=['id'],
1372 left_joins=[('Issue2Label ON Issue.id = Issue2Label.issue_id', [])],
1373 label_id=[123, 456], project_id=789,
1374 where=[('shard = %s', [1])]
1375 ).AndReturn([(1,), (2,), (3,)])
1376
1377 def testGetIIDsByLabelIDs(self):
1378 self.SetUpGetIIDsByLabelIDs()
1379 self.mox.ReplayAll()
1380 iids = self.services.issue.GetIIDsByLabelIDs(self.cnxn, [123, 456], 789, 1)
1381 self.mox.VerifyAll()
1382 self.assertEqual([1, 2, 3], iids)
1383
1384 def SetUpGetIIDsByParticipant(self):
1385 self.services.issue.issue_tbl.Select(
1386 self.cnxn, shard_id=1, cols=['id'],
1387 reporter_id=[111L, 888L],
1388 where=[('shard = %s', [1]), ('Issue.project_id IN (%s)', [789])]
1389 ).AndReturn([(1,)])
1390 self.services.issue.issue_tbl.Select(
1391 self.cnxn, shard_id=1, cols=['id'],
1392 owner_id=[111L, 888L],
1393 where=[('shard = %s', [1]), ('Issue.project_id IN (%s)', [789])]
1394 ).AndReturn([(2,)])
1395 self.services.issue.issue_tbl.Select(
1396 self.cnxn, shard_id=1, cols=['id'],
1397 derived_owner_id=[111L, 888L],
1398 where=[('shard = %s', [1]), ('Issue.project_id IN (%s)', [789])]
1399 ).AndReturn([(3,)])
1400 self.services.issue.issue_tbl.Select(
1401 self.cnxn, shard_id=1, cols=['id'],
1402 left_joins=[('Issue2Cc ON Issue2Cc.issue_id = Issue.id', [])],
1403 cc_id=[111L, 888L],
1404 where=[('shard = %s', [1]), ('Issue.project_id IN (%s)', [789]),
1405 ('cc_id IS NOT NULL', [])]
1406 ).AndReturn([(4,)])
1407 self.services.issue.issue_tbl.Select(
1408 self.cnxn, shard_id=1, cols=['Issue.id'],
1409 left_joins=[
1410 ('Issue2FieldValue ON Issue.id = Issue2FieldValue.issue_id', []),
1411 ('FieldDef ON Issue2FieldValue.field_id = FieldDef.id', [])],
1412 user_id=[111L, 888L], grants_perm='View',
1413 where=[('shard = %s', [1]), ('Issue.project_id IN (%s)', [789]),
1414 ('user_id IS NOT NULL', [])]
1415 ).AndReturn([(5,)])
1416
1417 def testGetIIDsByParticipant(self):
1418 self.SetUpGetIIDsByParticipant()
1419 self.mox.ReplayAll()
1420 iids = self.services.issue.GetIIDsByParticipant(
1421 self.cnxn, [111L, 888L], [789], 1)
1422 self.mox.VerifyAll()
1423 self.assertEqual([1, 2, 3, 4, 5], iids)
1424
1425
1426 class IssueServiceFunctionsTest(unittest.TestCase):
1427
1428 def testUpdateClosedTimestamp(self):
1429 config = tracker_pb2.ProjectIssueConfig()
1430 config.well_known_statuses.append(tracker_pb2.StatusDef(
1431 status='New', means_open=True))
1432 config.well_known_statuses.append(tracker_pb2.StatusDef(
1433 status='Accepted', means_open=True))
1434 config.well_known_statuses.append(tracker_pb2.StatusDef(
1435 status='Old', means_open=False))
1436 config.well_known_statuses.append(tracker_pb2.StatusDef(
1437 status='Closed', means_open=False))
1438
1439 issue = tracker_pb2.Issue()
1440 issue.local_id = 1234
1441 issue.status = 'New'
1442
1443 # ensure the default value is undef
1444 self.assert_(not issue.closed_timestamp)
1445
1446 # ensure transitioning to the same and other open states
1447 # doesn't set the timestamp
1448 issue.status = 'New'
1449 issue_svc._UpdateClosedTimestamp(config, issue, 'New')
1450 self.assert_(not issue.closed_timestamp)
1451
1452 issue.status = 'Accepted'
1453 issue_svc._UpdateClosedTimestamp(config, issue, 'New')
1454 self.assert_(not issue.closed_timestamp)
1455
1456 # ensure transitioning from open to closed sets the timestamp
1457 issue.status = 'Closed'
1458 issue_svc._UpdateClosedTimestamp(config, issue, 'Accepted')
1459 self.assert_(issue.closed_timestamp)
1460
1461 # ensure that the timestamp is cleared when transitioning from
1462 # closed to open
1463 issue.status = 'New'
1464 issue_svc._UpdateClosedTimestamp(config, issue, 'Closed')
1465 self.assert_(not issue.closed_timestamp)
1466
1467
1468 if __name__ == '__main__':
1469 unittest.main()
OLDNEW
« no previous file with comments | « appengine/monorail/services/test/fulltext_helpers_test.py ('k') | appengine/monorail/services/test/project_svc_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698