| # Copyright 2016 The Chromium Authors. All rights reserved. |
| # Use of this source code is govered by a BSD-style |
| # license that can be found in the LICENSE file or at |
| # https://developers.google.com/open-source/licenses/bsd |
| |
| """Unit tests for issue_svc module.""" |
| |
| import time |
| import unittest |
| |
| import mox |
| |
| from google.appengine.api import search |
| from google.appengine.ext import testbed |
| |
| import settings |
| from framework import sql |
| from proto import tracker_pb2 |
| from services import issue_svc |
| from services import service_manager |
| from services import spam_svc |
| from services import tracker_fulltext |
| from testing import fake |
| from tracker import tracker_bizobj |
| |
| |
| class MockIndex(object): |
| |
| def delete(self, string_list): |
| pass |
| |
| |
| def MakeIssueService(project_service, config_service, cache_manager, my_mox): |
| issue_service = issue_svc.IssueService( |
| project_service, config_service, cache_manager) |
| for table_var in [ |
| 'issue_tbl', 'issuesummary_tbl', 'issue2label_tbl', |
| 'issue2component_tbl', 'issue2cc_tbl', 'issue2notify_tbl', |
| 'issue2fieldvalue_tbl', 'issuerelation_tbl', 'danglingrelation_tbl', |
| 'issueformerlocations_tbl', 'comment_tbl', 'issueupdate_tbl', |
| 'attachment_tbl', 'reindexqueue_tbl', 'localidcounter_tbl']: |
| setattr(issue_service, table_var, my_mox.CreateMock(sql.SQLTableManager)) |
| |
| return issue_service |
| |
| |
| class IssueIDTwoLevelCacheTest(unittest.TestCase): |
| |
| def setUp(self): |
| self.mox = mox.Mox() |
| self.cnxn = 'fake connection' |
| self.project_service = fake.ProjectService() |
| self.config_service = fake.ConfigService() |
| self.cache_manager = fake.CacheManager() |
| self.issue_service = MakeIssueService( |
| self.project_service, self.config_service, self.cache_manager, |
| self.mox) |
| self.issue_id_2lc = self.issue_service.issue_id_2lc |
| self.spam_service = fake.SpamService() |
| |
| def tearDown(self): |
| self.mox.UnsetStubs() |
| self.mox.ResetAll() |
| |
| def testDeserializeIssueIDs_Empty(self): |
| issue_id_dict = self.issue_id_2lc._DeserializeIssueIDs([]) |
| self.assertEqual({}, issue_id_dict) |
| |
| def testDeserializeIssueIDs_Normal(self): |
| rows = [(789, 1, 78901), (789, 2, 78902), (789, 3, 78903)] |
| issue_id_dict = self.issue_id_2lc._DeserializeIssueIDs(rows) |
| expected = { |
| (789, 1): 78901, |
| (789, 2): 78902, |
| (789, 3): 78903, |
| } |
| self.assertEqual(expected, issue_id_dict) |
| |
| def SetUpFetchItems(self): |
| where = [ |
| ('(Issue.project_id = %s AND Issue.local_id IN (%s,%s,%s))', |
| [789, 1, 2, 3])] |
| rows = [(789, 1, 78901), (789, 2, 78902), (789, 3, 78903)] |
| self.issue_service.issue_tbl.Select( |
| self.cnxn, cols=['project_id', 'local_id', 'id'], |
| where=where, or_where_conds=True).AndReturn(rows) |
| |
| def testFetchItems(self): |
| project_local_ids_list = [(789, 1), (789, 2), (789, 3)] |
| issue_ids = [78901, 78902, 78903] |
| self.SetUpFetchItems() |
| self.mox.ReplayAll() |
| issue_dict = self.issue_id_2lc.FetchItems( |
| self.cnxn, project_local_ids_list) |
| self.mox.VerifyAll() |
| self.assertItemsEqual(project_local_ids_list, issue_dict.keys()) |
| self.assertItemsEqual(issue_ids, issue_dict.values()) |
| |
| def testKeyToStr(self): |
| self.assertEqual('789,1', self.issue_id_2lc._KeyToStr((789, 1))) |
| |
| def testStrToKey(self): |
| self.assertEqual((789, 1), self.issue_id_2lc._StrToKey('789,1')) |
| |
| |
| class IssueTwoLevelCacheTest(unittest.TestCase): |
| |
| def setUp(self): |
| self.mox = mox.Mox() |
| self.cnxn = 'fake connection' |
| self.project_service = fake.ProjectService() |
| self.config_service = fake.ConfigService() |
| self.cache_manager = fake.CacheManager() |
| self.issue_service = MakeIssueService( |
| self.project_service, self.config_service, self.cache_manager, |
| self.mox) |
| self.issue_2lc = self.issue_service.issue_2lc |
| |
| now = int(time.time()) |
| self.project_service.TestAddProject('proj', project_id=789) |
| self.issue_rows = [ |
| (78901, 789, 1, 1, 111L, 222L, now, now, now, 0, 0, 0, 1, 0, False)] |
| self.summary_rows = [(78901, 'sum')] |
| self.label_rows = [(78901, 1, 0)] |
| self.component_rows = [] |
| self.cc_rows = [(78901, 333L, 0)] |
| self.notify_rows = [] |
| self.fieldvalue_rows = [] |
| self.relation_rows = [ |
| (78901, 78902, 'blockedon'), (78903, 78901, 'blockedon')] |
| self.dangling_relation_rows = [ |
| (78901, 'codesite', 5001, 'blocking'), |
| (78901, 'codesite', 5002, 'blockedon')] |
| |
| def tearDown(self): |
| self.mox.UnsetStubs() |
| self.mox.ResetAll() |
| |
| def testDeserializeIssues_Empty(self): |
| issue_dict = self.issue_2lc._DeserializeIssues( |
| self.cnxn, [], [], [], [], [], [], [], [], []) |
| self.assertEqual({}, issue_dict) |
| |
| def testDeserializeIssues_Normal(self): |
| issue_dict = self.issue_2lc._DeserializeIssues( |
| self.cnxn, self.issue_rows, self.summary_rows, self.label_rows, |
| self.component_rows, self.cc_rows, self.notify_rows, |
| self.fieldvalue_rows, self.relation_rows, self.dangling_relation_rows) |
| self.assertItemsEqual([78901], issue_dict.keys()) |
| |
| def testDeserializeIssues_UnexpectedLabel(self): |
| unexpected_label_rows = [ |
| (78901, 999, 0) |
| ] |
| self.assertRaises( |
| AssertionError, |
| self.issue_2lc._DeserializeIssues, |
| self.cnxn, self.issue_rows, self.summary_rows, unexpected_label_rows, |
| self.component_rows, self.cc_rows, self.notify_rows, |
| self.fieldvalue_rows, self.relation_rows, self.dangling_relation_rows) |
| |
| def testDeserializeIssues_UnexpectedIssueRelation(self): |
| unexpected_relation_rows = [ |
| (78990, 78999, 'blockedon') |
| ] |
| self.assertRaises( |
| AssertionError, |
| self.issue_2lc._DeserializeIssues, |
| self.cnxn, self.issue_rows, self.summary_rows, self.label_rows, |
| self.component_rows, self.cc_rows, self.notify_rows, |
| self.fieldvalue_rows, unexpected_relation_rows, |
| self.dangling_relation_rows) |
| |
| def SetUpFetchItems(self, issue_ids): |
| shard_id = None |
| self.issue_service.issue_tbl.Select( |
| self.cnxn, cols=issue_svc.ISSUE_COLS, id=issue_ids, |
| shard_id=shard_id).AndReturn(self.issue_rows) |
| self.issue_service.issuesummary_tbl.Select( |
| self.cnxn, cols=issue_svc.ISSUESUMMARY_COLS, shard_id=shard_id, |
| issue_id=issue_ids).AndReturn(self.summary_rows) |
| self.issue_service.issue2label_tbl.Select( |
| self.cnxn, cols=issue_svc.ISSUE2LABEL_COLS, shard_id=shard_id, |
| issue_id=issue_ids).AndReturn(self.label_rows) |
| self.issue_service.issue2component_tbl.Select( |
| self.cnxn, cols=issue_svc.ISSUE2COMPONENT_COLS, shard_id=shard_id, |
| issue_id=issue_ids).AndReturn(self.component_rows) |
| self.issue_service.issue2cc_tbl.Select( |
| self.cnxn, cols=issue_svc.ISSUE2CC_COLS, shard_id=shard_id, |
| issue_id=issue_ids).AndReturn(self.cc_rows) |
| self.issue_service.issue2notify_tbl.Select( |
| self.cnxn, cols=issue_svc.ISSUE2NOTIFY_COLS, shard_id=shard_id, |
| issue_id=issue_ids).AndReturn(self.notify_rows) |
| self.issue_service.issue2fieldvalue_tbl.Select( |
| self.cnxn, cols=issue_svc.ISSUE2FIELDVALUE_COLS, shard_id=shard_id, |
| issue_id=issue_ids).AndReturn(self.fieldvalue_rows) |
| self.issue_service.issuerelation_tbl.Select( |
| self.cnxn, cols=issue_svc.ISSUERELATION_COLS, # Note: no shard |
| where=[('(issue_id IN (%s) OR dst_issue_id IN (%s))', |
| issue_ids + issue_ids)]).AndReturn(self.relation_rows) |
| self.issue_service.danglingrelation_tbl.Select( |
| self.cnxn, cols=issue_svc.DANGLINGRELATION_COLS, # Note: no shard |
| issue_id=issue_ids).AndReturn(self.dangling_relation_rows) |
| |
| def testFetchItems(self): |
| issue_ids = [78901] |
| self.SetUpFetchItems(issue_ids) |
| self.mox.ReplayAll() |
| issue_dict = self.issue_2lc.FetchItems(self.cnxn, issue_ids) |
| self.mox.VerifyAll() |
| self.assertItemsEqual(issue_ids, issue_dict.keys()) |
| |
| |
| class IssueServiceTest(unittest.TestCase): |
| |
| def setUp(self): |
| self.testbed = testbed.Testbed() |
| self.testbed.activate() |
| self.testbed.init_memcache_stub() |
| |
| self.mox = mox.Mox() |
| self.cnxn = self.mox.CreateMock(sql.MonorailConnection) |
| self.services = service_manager.Services() |
| self.services.user = fake.UserService() |
| self.services.project = fake.ProjectService() |
| self.services.config = fake.ConfigService() |
| self.services.features = fake.FeaturesService() |
| self.cache_manager = fake.CacheManager() |
| self.services.issue = MakeIssueService( |
| self.services.project, self.services.config, self.cache_manager, |
| self.mox) |
| self.services.spam = self.mox.CreateMock(spam_svc.SpamService) |
| self.now = int(time.time()) |
| self.orig_index_issues = tracker_fulltext.IndexIssues |
| tracker_fulltext.IndexIssues = lambda *args: None |
| |
| def classifierResult(self, label, score): |
| return {'outputLabel': label, |
| 'outputMulti': [{'label': label, 'score': score}]} |
| |
| def tearDown(self): |
| self.testbed.deactivate() |
| self.mox.UnsetStubs() |
| self.mox.ResetAll() |
| tracker_fulltext.IndexIssues = self.orig_index_issues |
| |
| ### Issue ID lookups |
| |
| def testLookupIssueIDs_Hit(self): |
| self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901) |
| self.services.issue.issue_id_2lc.CacheItem((789, 2), 78902) |
| actual = self.services.issue.LookupIssueIDs( |
| self.cnxn, [(789, 1), (789, 2)]) |
| self.assertEqual([78901, 78902], actual) |
| |
| def testLookupIssueID(self): |
| self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901) |
| actual = self.services.issue.LookupIssueID(self.cnxn, 789, 1) |
| self.assertEqual(78901, actual) |
| |
| def testResolveIssueRefs(self): |
| self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901) |
| self.services.issue.issue_id_2lc.CacheItem((789, 2), 78902) |
| prefetched_projects = {'proj': fake.Project('proj', project_id=789)} |
| refs = [('proj', 1), (None, 2)] |
| actual = self.services.issue.ResolveIssueRefs( |
| self.cnxn, prefetched_projects, 'proj', refs) |
| self.assertEqual([78901, 78902], actual) |
| |
| ### Issue objects |
| |
| def testCreateIssue(self): |
| settings.classifier_spam_thresh = 0.9 |
| self.SetUpAllocateNextLocalID(789, None, None) |
| self.SetUpInsertIssue() |
| self.SetUpInsertComment(7890101, True) |
| self.services.spam.ClassifyIssue(mox.IgnoreArg(), |
| mox.IgnoreArg()).AndReturn( |
| self.classifierResult('ham', 1.0)) |
| self.services.spam.RecordClassifierIssueVerdict(self.cnxn, |
| mox.IsA(tracker_pb2.Issue), False, 1.0) |
| self.SetUpUpdateIssuesModified(set()) |
| |
| self.mox.ReplayAll() |
| actual_local_id = self.services.issue.CreateIssue( |
| self.cnxn, self.services, 789, 'sum', |
| 'New', 111L, [], ['Type-Defect'], [], [], 111L, 'content', |
| index_now=False, timestamp=self.now) |
| self.mox.VerifyAll() |
| self.assertEqual(1, actual_local_id) |
| |
| def testCreateIssue_EmptyStringLabels(self): |
| settings.classifier_spam_thresh = 0.9 |
| self.SetUpAllocateNextLocalID(789, None, None) |
| self.SetUpInsertIssue(label_rows=[]) |
| self.SetUpInsertComment(7890101, True) |
| self.services.spam.ClassifyIssue(mox.IgnoreArg(), |
| mox.IgnoreArg()).AndReturn( |
| self.classifierResult('ham', 1.0)) |
| self.services.spam.RecordClassifierIssueVerdict(self.cnxn, |
| mox.IsA(tracker_pb2.Issue), False, 1.0) |
| self.SetUpUpdateIssuesModified(set(), modified_timestamp=self.now) |
| |
| self.mox.ReplayAll() |
| actual_local_id = self.services.issue.CreateIssue( |
| self.cnxn, self.services, 789, 'sum', |
| 'New', 111L, [], [',', '', ' ', ', '], [], [], 111L, 'content', |
| index_now=False, timestamp=self.now) |
| self.mox.VerifyAll() |
| self.assertEqual(1, actual_local_id) |
| |
| def SetUpUpdateIssuesModified(self, iids, modified_timestamp=None): |
| self.services.issue.issue_tbl.Update( |
| self.cnxn, {'modified': modified_timestamp or self.now}, |
| id=iids, commit=False) |
| |
| def testCreateIssue_spam(self): |
| settings.classifier_spam_thresh = 0.9 |
| self.SetUpAllocateNextSpamID(789, None, None) |
| self.SetUpInsertSpamIssue() |
| self.SetUpInsertComment(7890101, True) |
| |
| self.services.spam.ClassifyIssue(mox.IsA(tracker_pb2.Issue), |
| mox.IsA(tracker_pb2.IssueComment)).AndReturn( |
| self.classifierResult('spam', 1.0)) |
| self.services.spam.RecordClassifierIssueVerdict(self.cnxn, |
| mox.IsA(tracker_pb2.Issue), True, 1.0) |
| self.SetUpUpdateIssuesModified(set()) |
| |
| self.mox.ReplayAll() |
| actual_local_id = self.services.issue.CreateIssue( |
| self.cnxn, self.services, 789, 'sum', |
| 'New', 111L, [], ['Type-Defect'], [], [], 111L, 'content', |
| index_now=False, timestamp=self.now) |
| self.mox.VerifyAll() |
| self.assertEqual(-1, actual_local_id) |
| |
| def testGetAllIssuesInProject_NoIssues(self): |
| self.SetUpGetHighestLocalID(789, None, None) |
| self.mox.ReplayAll() |
| issues = self.services.issue.GetAllIssuesInProject(self.cnxn, 789) |
| self.mox.VerifyAll() |
| self.assertEqual([], issues) |
| |
| def testGetAnyOnHandIssue(self): |
| issue_ids = [78901, 78902, 78903] |
| self.SetUpGetIssues() |
| issue = self.services.issue.GetAnyOnHandIssue(issue_ids) |
| self.assertEqual(78901, issue.issue_id) |
| |
| def SetUpGetIssues(self): |
| issue_1 = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', issue_id=78901) |
| issue_1.project_name = 'proj' |
| issue_2 = fake.MakeTestIssue( |
| project_id=789, local_id=2, owner_id=111L, summary='sum', |
| status='Fixed', issue_id=78902) |
| issue_2.project_name = 'proj' |
| self.services.issue.issue_2lc.CacheItem(78901, issue_1) |
| self.services.issue.issue_2lc.CacheItem(78902, issue_2) |
| return issue_1, issue_2 |
| |
| def testGetIssuesDict(self): |
| issue_ids = [78901, 78902] |
| issue_1, issue_2 = self.SetUpGetIssues() |
| issues_dict = self.services.issue.GetIssuesDict(self.cnxn, issue_ids) |
| self.assertEqual( |
| {78901: issue_1, 78902: issue_2}, |
| issues_dict) |
| |
| def testGetIssues(self): |
| issue_ids = [78901, 78902] |
| issue_1, issue_2 = self.SetUpGetIssues() |
| issues = self.services.issue.GetIssues(self.cnxn, issue_ids) |
| self.assertEqual([issue_1, issue_2], issues) |
| |
| def testGetIssue(self): |
| issue_1, _issue_2 = self.SetUpGetIssues() |
| actual_issue = self.services.issue.GetIssue(self.cnxn, 78901) |
| self.assertEqual(issue_1, actual_issue) |
| |
| def testGetIssuesByLocalIDs(self): |
| issue_1, issue_2 = self.SetUpGetIssues() |
| self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901) |
| self.services.issue.issue_id_2lc.CacheItem((789, 2), 78902) |
| actual_issues = self.services.issue.GetIssuesByLocalIDs( |
| self.cnxn, 789, [1, 2]) |
| self.assertEqual([issue_1, issue_2], actual_issues) |
| |
| def testGetIssueByLocalID(self): |
| issue_1, _issue_2 = self.SetUpGetIssues() |
| self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901) |
| actual_issues = self.services.issue.GetIssueByLocalID(self.cnxn, 789, 1) |
| self.assertEqual(issue_1, actual_issues) |
| |
| def testGetOpenAndClosedIssues(self): |
| issue_1, issue_2 = self.SetUpGetIssues() |
| open_issues, closed_issues = self.services.issue.GetOpenAndClosedIssues( |
| self.cnxn, [78901, 78902]) |
| self.assertEqual([issue_1], open_issues) |
| self.assertEqual([issue_2], closed_issues) |
| |
| def SetUpGetCurrentLocationOfMovedIssue(self, project_id, local_id): |
| issue_id = project_id * 100 + local_id |
| self.services.issue.issueformerlocations_tbl.SelectValue( |
| self.cnxn, 'issue_id', default=0, project_id=project_id, |
| local_id=local_id).AndReturn(issue_id) |
| self.services.issue.issue_tbl.SelectRow( |
| self.cnxn, cols=['project_id', 'local_id'], id=issue_id).AndReturn( |
| (project_id + 1, local_id + 1)) |
| |
| def testGetCurrentLocationOfMovedIssue(self): |
| self.SetUpGetCurrentLocationOfMovedIssue(789, 1) |
| self.mox.ReplayAll() |
| new_project_id, new_local_id = ( |
| self.services.issue.GetCurrentLocationOfMovedIssue(self.cnxn, 789, 1)) |
| self.mox.VerifyAll() |
| self.assertEqual(789 + 1, new_project_id) |
| self.assertEqual(1 + 1, new_local_id) |
| |
| def SetUpGetPreviousLocations(self, issue_id, location_rows): |
| self.services.issue.issueformerlocations_tbl.Select( |
| self.cnxn, cols=['project_id', 'local_id'], |
| issue_id=issue_id).AndReturn(location_rows) |
| |
| def testGetPreviousLocations(self): |
| self.SetUpGetPreviousLocations(78901, [(781, 1), (782, 11), (789, 1)]) |
| self.mox.ReplayAll() |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', issue_id=78901) |
| locations = self.services.issue.GetPreviousLocations(self.cnxn, issue) |
| self.mox.VerifyAll() |
| self.assertEqual(locations, [(781, 1), (782, 11)]) |
| |
| def SetUpInsertIssue(self, label_rows=None): |
| row = (789, 1, 1, 111L, 111L, self.now, 0, self.now, None, 0, |
| False, 0, 0, False) |
| self.services.issue.issue_tbl.InsertRows( |
| self.cnxn, issue_svc.ISSUE_COLS[1:], [row], |
| commit=False, return_generated_ids=True).AndReturn([78901]) |
| self.cnxn.Commit() |
| self.services.issue.issue_tbl.Update( |
| self.cnxn, {'shard': 78901 % settings.num_logical_shards}, |
| id=78901, commit=False) |
| self.SetUpUpdateIssuesSummary() |
| self.SetUpUpdateIssuesLabels(label_rows=label_rows) |
| self.SetUpUpdateIssuesFields() |
| self.SetUpUpdateIssuesComponents() |
| self.SetUpUpdateIssuesCc() |
| self.SetUpUpdateIssuesNotify() |
| self.SetUpUpdateIssuesRelation() |
| |
| def SetUpInsertSpamIssue(self): |
| row = (789, -1, 1, 111L, 111L, self.now, 0, self.now, None, 0, |
| False, 0, 0, True) |
| self.services.issue.issue_tbl.InsertRows( |
| self.cnxn, issue_svc.ISSUE_COLS[1:], [row], |
| commit=False, return_generated_ids=True).AndReturn([78901]) |
| self.cnxn.Commit() |
| self.services.issue.issue_tbl.Update( |
| self.cnxn, {'shard': 78901 % settings.num_logical_shards}, |
| id=78901, commit=False) |
| self.SetUpUpdateIssuesSummary() |
| self.SetUpUpdateIssuesLabels() |
| self.SetUpUpdateIssuesFields() |
| self.SetUpUpdateIssuesComponents() |
| self.SetUpUpdateIssuesCc() |
| self.SetUpUpdateIssuesNotify() |
| self.SetUpUpdateIssuesRelation() |
| |
| def SetUpUpdateIssuesSummary(self): |
| self.services.issue.issuesummary_tbl.InsertRows( |
| self.cnxn, ['issue_id', 'summary'], |
| [(78901, 'sum')], replace=True, commit=False) |
| |
| def SetUpUpdateIssuesLabels(self, label_rows=None): |
| if label_rows is None: |
| label_rows = [(78901, 1, False, 1)] |
| self.services.issue.issue2label_tbl.Delete( |
| self.cnxn, issue_id=[78901], commit=False) |
| self.services.issue.issue2label_tbl.InsertRows( |
| self.cnxn, ['issue_id', 'label_id', 'derived', 'issue_shard'], |
| label_rows, ignore=True, commit=False) |
| |
| def SetUpUpdateIssuesFields(self, issue2fieldvalue_rows=None): |
| issue2fieldvalue_rows = issue2fieldvalue_rows or [] |
| self.services.issue.issue2fieldvalue_tbl.Delete( |
| self.cnxn, issue_id=[78901], commit=False) |
| self.services.issue.issue2fieldvalue_tbl.InsertRows( |
| self.cnxn, issue_svc.ISSUE2FIELDVALUE_COLS + ['issue_shard'], |
| issue2fieldvalue_rows, commit=False) |
| |
| def SetUpUpdateIssuesComponents(self, issue2component_rows=None): |
| issue2component_rows = issue2component_rows or [] |
| self.services.issue.issue2component_tbl.Delete( |
| self.cnxn, issue_id=[78901], commit=False) |
| self.services.issue.issue2component_tbl.InsertRows( |
| self.cnxn, ['issue_id', 'component_id', 'derived', 'issue_shard'], |
| issue2component_rows, ignore=True, commit=False) |
| |
| def SetUpUpdateIssuesCc(self, issue2cc_rows=None): |
| issue2cc_rows = issue2cc_rows or [] |
| self.services.issue.issue2cc_tbl.Delete( |
| self.cnxn, issue_id=[78901], commit=False) |
| self.services.issue.issue2cc_tbl.InsertRows( |
| self.cnxn, ['issue_id', 'cc_id', 'derived', 'issue_shard'], |
| issue2cc_rows, ignore=True, commit=False) |
| |
| def SetUpUpdateIssuesNotify(self, notify_rows=None): |
| notify_rows = notify_rows or [] |
| self.services.issue.issue2notify_tbl.Delete( |
| self.cnxn, issue_id=[78901], commit=False) |
| self.services.issue.issue2notify_tbl.InsertRows( |
| self.cnxn, issue_svc.ISSUE2NOTIFY_COLS, |
| notify_rows, ignore=True, commit=False) |
| |
| def SetUpUpdateIssuesRelation( |
| self, relation_rows=None, dangling_relation_rows=None): |
| relation_rows = relation_rows or [] |
| dangling_relation_rows = dangling_relation_rows or [] |
| self.services.issue.issuerelation_tbl.Delete( |
| self.cnxn, issue_id=[78901], commit=False) |
| self.services.issue.issuerelation_tbl.Delete( |
| self.cnxn, dst_issue_id=[78901], kind='blockedon', |
| commit=False) |
| self.services.issue.issuerelation_tbl.InsertRows( |
| self.cnxn, issue_svc.ISSUERELATION_COLS, relation_rows, |
| ignore=True, commit=False) |
| self.services.issue.danglingrelation_tbl.Delete( |
| self.cnxn, issue_id=[78901], commit=False) |
| self.services.issue.danglingrelation_tbl.InsertRows( |
| self.cnxn, issue_svc.DANGLINGRELATION_COLS, dangling_relation_rows, |
| ignore=True, commit=False) |
| |
| def testInsertIssue(self): |
| self.SetUpInsertIssue() |
| self.mox.ReplayAll() |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, reporter_id=111L, |
| summary='sum', status='New', labels=['Type-Defect'], issue_id=78901, |
| opened_timestamp=self.now, modified_timestamp=self.now) |
| actual_issue_id = self.services.issue.InsertIssue(self.cnxn, issue) |
| self.mox.VerifyAll() |
| self.assertEqual(78901, actual_issue_id) |
| |
| def SetUpUpdateIssues(self, given_delta=None): |
| delta = given_delta or { |
| 'project_id': 789, |
| 'local_id': 1, |
| 'owner_id': 111L, |
| 'status_id': 1, |
| 'opened': 123456789, |
| 'closed': 0, |
| 'modified': 123456789, |
| 'derived_owner_id': None, |
| 'derived_status_id': None, |
| 'deleted': False, |
| 'star_count': 12, |
| 'attachment_count': 0, |
| 'is_spam': False, |
| } |
| self.services.issue.issue_tbl.Update( |
| self.cnxn, delta, id=78901, commit=False) |
| if not given_delta: |
| self.SetUpUpdateIssuesLabels() |
| self.SetUpUpdateIssuesCc() |
| self.SetUpUpdateIssuesFields() |
| self.SetUpUpdateIssuesComponents() |
| self.SetUpUpdateIssuesNotify() |
| self.SetUpUpdateIssuesSummary() |
| self.SetUpUpdateIssuesRelation() |
| |
| self.cnxn.Commit() |
| |
| def testUpdateIssues_Empty(self): |
| # Note: no setup because DB should not be called. |
| self.mox.ReplayAll() |
| self.services.issue.UpdateIssues(self.cnxn, []) |
| self.mox.VerifyAll() |
| |
| def testUpdateIssues_Normal(self): |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', labels=['Type-Defect'], issue_id=78901, |
| opened_timestamp=123456789, modified_timestamp=123456789, |
| star_count=12) |
| self.SetUpUpdateIssues() |
| self.mox.ReplayAll() |
| self.services.issue.UpdateIssues(self.cnxn, [issue]) |
| self.mox.VerifyAll() |
| |
| def testUpdateIssue(self): |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', labels=['Type-Defect'], issue_id=78901, |
| opened_timestamp=123456789, modified_timestamp=123456789, |
| star_count=12) |
| self.SetUpUpdateIssues() |
| self.mox.ReplayAll() |
| self.services.issue.UpdateIssue(self.cnxn, issue) |
| self.mox.VerifyAll() |
| |
| def testUpdateIssuesSummary(self): |
| issue = fake.MakeTestIssue( |
| local_id=1, issue_id=78901, owner_id=111L, summary='sum', status='New', |
| project_id=789) |
| self.SetUpUpdateIssuesSummary() |
| self.mox.ReplayAll() |
| self.services.issue._UpdateIssuesSummary(self.cnxn, [issue], commit=False) |
| self.mox.VerifyAll() |
| |
| def testUpdateIssuesLabels(self): |
| issue = fake.MakeTestIssue( |
| local_id=1, issue_id=78901, owner_id=111L, summary='sum', status='New', |
| labels=['Type-Defect'], project_id=789) |
| self.SetUpUpdateIssuesLabels() |
| self.mox.ReplayAll() |
| self.services.issue._UpdateIssuesLabels( |
| self.cnxn, [issue], 789, commit=False) |
| self.mox.VerifyAll() |
| |
| def testUpdateIssuesFields_Empty(self): |
| issue = fake.MakeTestIssue( |
| local_id=1, issue_id=78901, owner_id=111L, summary='sum', status='New', |
| project_id=789) |
| self.SetUpUpdateIssuesFields() |
| self.mox.ReplayAll() |
| self.services.issue._UpdateIssuesFields(self.cnxn, [issue], commit=False) |
| self.mox.VerifyAll() |
| |
| def testUpdateIssuesFields_Some(self): |
| issue = fake.MakeTestIssue( |
| local_id=1, issue_id=78901, owner_id=111L, summary='sum', status='New', |
| project_id=789) |
| issue_shard = issue.issue_id % settings.num_logical_shards |
| fv1 = tracker_bizobj.MakeFieldValue(345, 679, '', 0L, False) |
| issue.field_values.append(fv1) |
| fv2 = tracker_bizobj.MakeFieldValue(346, 0, 'Blue', 0L, True) |
| issue.field_values.append(fv2) |
| self.SetUpUpdateIssuesFields(issue2fieldvalue_rows=[ |
| (issue.issue_id, fv1.field_id, fv1.int_value, fv1.str_value, |
| None, fv1.derived, issue_shard), |
| (issue.issue_id, fv2.field_id, fv2.int_value, fv2.str_value, |
| None, fv2.derived, issue_shard), |
| ]) |
| self.mox.ReplayAll() |
| self.services.issue._UpdateIssuesFields(self.cnxn, [issue], commit=False) |
| self.mox.VerifyAll() |
| |
| def testUpdateIssuesComponents_Empty(self): |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', issue_id=78901) |
| self.SetUpUpdateIssuesComponents() |
| self.mox.ReplayAll() |
| self.services.issue._UpdateIssuesComponents( |
| self.cnxn, [issue], commit=False) |
| self.mox.VerifyAll() |
| |
| def testUpdateIssuesCc_Empty(self): |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', issue_id=78901) |
| self.SetUpUpdateIssuesCc() |
| self.mox.ReplayAll() |
| self.services.issue._UpdateIssuesCc(self.cnxn, [issue], commit=False) |
| self.mox.VerifyAll() |
| |
| def testUpdateIssuesCc_Some(self): |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', issue_id=78901) |
| issue.cc_ids = [222L, 333L] |
| issue.derived_cc_ids = [888L] |
| issue_shard = issue.issue_id % settings.num_logical_shards |
| self.SetUpUpdateIssuesCc(issue2cc_rows=[ |
| (issue.issue_id, 222L, False, issue_shard), |
| (issue.issue_id, 333L, False, issue_shard), |
| (issue.issue_id, 888L, True, issue_shard), |
| ]) |
| self.mox.ReplayAll() |
| self.services.issue._UpdateIssuesCc(self.cnxn, [issue], commit=False) |
| self.mox.VerifyAll() |
| |
| def testUpdateIssuesNotify_Empty(self): |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', issue_id=78901) |
| self.SetUpUpdateIssuesNotify() |
| self.mox.ReplayAll() |
| self.services.issue._UpdateIssuesNotify(self.cnxn, [issue], commit=False) |
| self.mox.VerifyAll() |
| |
| def testUpdateIssuesRelation_Empty(self): |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', issue_id=78901) |
| self.SetUpUpdateIssuesRelation() |
| self.mox.ReplayAll() |
| self.services.issue._UpdateIssuesRelation(self.cnxn, [issue], commit=False) |
| self.mox.VerifyAll() |
| |
| def testDeltaUpdateIssue(self): |
| pass # TODO(jrobbins): write more tests |
| |
| def testDeltaUpdateIssue_MergedInto(self): |
| commenter_id = 222L |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', issue_id=78901, project_name='proj') |
| target_issue = fake.MakeTestIssue( |
| project_id=789, local_id=2, owner_id=111L, summary='sum sum', |
| status='Live', issue_id=78902, project_name='proj') |
| config = tracker_bizobj.MakeDefaultProjectIssueConfig(789) |
| |
| self.mox.StubOutWithMock(self.services.issue, 'GetIssue') |
| self.mox.StubOutWithMock(self.services.issue, 'UpdateIssue') |
| self.mox.StubOutWithMock(self.services.issue, 'CreateIssueComment') |
| self.mox.StubOutWithMock(self.services.issue, '_UpdateIssuesModified') |
| |
| self.services.issue.GetIssue( |
| self.cnxn, 0).AndRaise(issue_svc.NoSuchIssueException) |
| self.services.issue.GetIssue( |
| self.cnxn, target_issue.issue_id).AndReturn(target_issue) |
| self.services.issue.UpdateIssue( |
| self.cnxn, issue, commit=False, invalidate=False) |
| amendments = [ |
| tracker_bizobj.MakeMergedIntoAmendment( |
| ('proj', 2), None, default_project_name='proj')] |
| self.services.issue.CreateIssueComment( |
| self.cnxn, 789, 1, commenter_id, 'comment text', |
| amendments=amendments, commit=False) |
| self.services.issue._UpdateIssuesModified( |
| self.cnxn, {issue.issue_id, target_issue.issue_id}, |
| modified_timestamp=self.now, invalidate=True) |
| |
| self.mox.ReplayAll() |
| self.services.issue.DeltaUpdateIssue( |
| self.cnxn, self.services, commenter_id, issue.project_id, config, |
| issue, issue.status, issue.owner_id, |
| [], [], [], [], [], [], [], [], [], |
| merged_into=target_issue.issue_id, comment='comment text', |
| index_now=False, timestamp=self.now) |
| self.mox.VerifyAll() |
| |
| def testApplyIssueComment(self): |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', issue_id=78901) |
| |
| self.mox.StubOutWithMock(self.services.issue, 'GetIssueByLocalID') |
| self.mox.StubOutWithMock(self.services.issue, 'UpdateIssues') |
| self.mox.StubOutWithMock(self.services.issue, 'GetCommentsForIssue') |
| self.mox.StubOutWithMock(self.services.issue, 'SoftDeleteComment') |
| self.mox.StubOutWithMock(self.services.issue, "CreateIssueComment") |
| self.mox.StubOutWithMock(self.services.issue, "_UpdateIssuesModified") |
| |
| self.services.issue.GetIssueByLocalID(self.cnxn, issue.project_id, |
| issue.local_id).AndReturn(issue) |
| self.services.issue.CreateIssueComment(self.cnxn, issue.project_id, |
| issue.local_id, issue.reporter_id, 'comment text', |
| amendments=[], attachments=None, inbound_message=None, |
| is_spam=False) |
| self.services.issue.UpdateIssues(self.cnxn, [issue], |
| just_derived=False, update_cols=None, commit=True, invalidate=True) |
| self.services.spam.ClassifyComment('comment text').AndReturn( |
| self.classifierResult('ham', 1.0)) |
| self.services.spam.RecordClassifierCommentVerdict(self.cnxn, |
| None, False, 1.0) |
| self.services.issue._UpdateIssuesModified( |
| self.cnxn, set(), modified_timestamp=self.now) |
| |
| self.mox.ReplayAll() |
| self.services.issue.ApplyIssueComment(self.cnxn, self.services, |
| issue.reporter_id, issue.project_id, issue.local_id, issue.summary, |
| issue.status, issue.owner_id, issue.cc_ids, issue.labels, |
| issue.field_values, issue.component_ids, [], |
| [], [], [], issue.merged_into, comment='comment text', |
| timestamp=self.now) |
| self.mox.VerifyAll() |
| |
| def testApplyIssueComment_spam(self): |
| settings.classifier_spam_thresh = 0.5 |
| |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', issue_id=78901) |
| |
| self.mox.StubOutWithMock(self.services.issue, "GetIssueByLocalID") |
| self.mox.StubOutWithMock(self.services.issue, "UpdateIssues") |
| self.mox.StubOutWithMock(self.services.issue, "GetCommentsForIssue") |
| self.mox.StubOutWithMock(self.services.issue, "CreateIssueComment") |
| self.mox.StubOutWithMock(self.services.issue, "SoftDeleteComment") |
| self.mox.StubOutWithMock(self.services.issue, "_UpdateIssuesModified") |
| |
| self.services.issue.GetIssueByLocalID(self.cnxn, 789, 1).AndReturn(issue) |
| self.services.issue.UpdateIssues(self.cnxn, [issue], |
| just_derived=False, update_cols=None, commit=True, invalidate=True) |
| self.services.issue.GetCommentsForIssue(self.cnxn, |
| issue.issue_id).AndReturn([""]) |
| self.services.issue.SoftDeleteComment(self.cnxn, |
| issue.project_id, issue.local_id, 0, issue.reporter_id, |
| self.services.user, is_spam=True) |
| self.services.spam.ClassifyComment('comment text').AndReturn( |
| self.classifierResult('spam', 1.0)) |
| self.services.spam.RecordClassifierCommentVerdict(self.cnxn, |
| mox.IsA(tracker_pb2.IssueComment), True, 1.0) |
| self.services.issue.CreateIssueComment(self.cnxn, issue.project_id, |
| issue.local_id, issue.reporter_id, 'comment text', |
| amendments=[], attachments=None, inbound_message=None, |
| is_spam=True).AndReturn(tracker_pb2.IssueComment()) |
| self.services.issue._UpdateIssuesModified( |
| self.cnxn, set(), modified_timestamp=self.now) |
| |
| self.mox.ReplayAll() |
| self.services.issue.ApplyIssueComment(self.cnxn, self.services, |
| issue.reporter_id, issue.project_id, issue.local_id, issue.summary, |
| issue.status, issue.owner_id, issue.cc_ids, issue.labels, |
| issue.field_values, issue.component_ids, [], |
| [], [], [], issue.merged_into, comment='comment text', |
| timestamp=self.now) |
| self.mox.VerifyAll() |
| |
| def testApplyIssueComment_blockedon(self): |
| issue = fake.MakeTestIssue( |
| project_id=789, local_id=1, owner_id=111L, summary='sum', |
| status='Live', issue_id=78901) |
| blockedon_issue = fake.MakeTestIssue( |
| project_id=789, local_id=2, owner_id=111L, summary='sum', |
| status='Live', issue_id=78902) |
| |
| self.mox.StubOutWithMock(self.services.issue, "GetIssueByLocalID") |
| self.mox.StubOutWithMock(self.services.issue, "UpdateIssues") |
| self.mox.StubOutWithMock(self.services.issue, "CreateIssueComment") |
| self.mox.StubOutWithMock(self.services.issue, "GetIssues") |
| self.mox.StubOutWithMock(self.services.issue, "_UpdateIssuesModified") |
| # Call to find added blockedon issues. |
| self.services.issue.GetIssues( |
| self.cnxn, [blockedon_issue.issue_id]).AndReturn([blockedon_issue]) |
| # Call to find removed blockedon issues. |
| self.services.issue.GetIssues(self.cnxn, []).AndReturn([]) |
| |
| self.services.issue.GetIssueByLocalID(self.cnxn, 789, 1).AndReturn(issue) |
| self.services.issue.UpdateIssues(self.cnxn, [issue], |
| just_derived=False, update_cols=None, commit=True, invalidate=True) |
| self.services.spam.ClassifyComment('comment text').AndReturn( |
| self.classifierResult('ham', 1.0)) |
| self.services.spam.RecordClassifierCommentVerdict(self.cnxn, |
| mox.IsA(tracker_pb2.IssueComment), False, 1.0) |
| self.services.issue.CreateIssueComment(self.cnxn, issue.project_id, |
| issue.local_id, issue.reporter_id, 'comment text', |
| amendments=[ |
| tracker_bizobj.MakeBlockedOnAmendment( |
| [(blockedon_issue.project_name, blockedon_issue.local_id)], [], |
| default_project_name=blockedon_issue.project_name)], |
| attachments=None, inbound_message=None, |
| is_spam=False).AndReturn(tracker_pb2.IssueComment()) |
| # Add a comment on the blockedon issue. |
| self.services.issue.CreateIssueComment( |
| self.cnxn, blockedon_issue.project_id, blockedon_issue.local_id, |
| blockedon_issue.reporter_id, content='', |
| amendments=[tracker_bizobj.MakeBlockingAmendment( |
| [(issue.project_name, issue.local_id)], [], |
| default_project_name=issue.project_name)]) |
| self.services.issue._UpdateIssuesModified( |
| self.cnxn, {blockedon_issue.issue_id}, modified_timestamp=self.now) |
| |
| self.mox.ReplayAll() |
| self.services.issue.ApplyIssueComment(self.cnxn, self.services, |
| issue.reporter_id, issue.project_id, issue.local_id, issue.summary, |
| issue.status, issue.owner_id, issue.cc_ids, issue.labels, |
| issue.field_values, issue.component_ids, [blockedon_issue.issue_id], |
| [], [], [], issue.merged_into, comment='comment text', |
| timestamp=self.now) |
| self.mox.VerifyAll() |
| |
| def SetUpMoveIssues_NewProject(self): |
| self.services.issue.issueformerlocations_tbl.Select( |
| self.cnxn, cols=issue_svc.ISSUEFORMERLOCATIONS_COLS, project_id=789, |
| issue_id=[78901]).AndReturn([]) |
| self.SetUpAllocateNextLocalID(789, None, None) |
| self.SetUpUpdateIssues() |
| self.services.issue.comment_tbl.Update( |
| self.cnxn, {'project_id': 789}, issue_id=[78901], commit=False) |
| |
| old_location_rows = [(78901, 711, 2)] |
| self.services.issue.issueformerlocations_tbl.InsertRows( |
| self.cnxn, issue_svc.ISSUEFORMERLOCATIONS_COLS, old_location_rows, |
| ignore=True, commit=False) |
| self.cnxn.Commit() |
| |
| def testMoveIssues_NewProject(self): |
| """Move project 711 issue 2 to become project 789 issue 1.""" |
| dest_project = fake.Project(project_id=789) |
| issue = fake.MakeTestIssue( |
| project_id=711, local_id=2, owner_id=111L, summary='sum', |
| status='Live', labels=['Type-Defect'], issue_id=78901, |
| opened_timestamp=123456789, modified_timestamp=123456789, |
| star_count=12) |
| self.SetUpMoveIssues_NewProject() |
| self.mox.ReplayAll() |
| self.services.issue.MoveIssues( |
| self.cnxn, dest_project, [issue], self.services.user) |
| self.mox.VerifyAll() |
| |
| # TODO(jrobbins): case where issue is moved back into former project |
| |
| def testExpungeFormerLocations(self): |
| self.services.issue.issueformerlocations_tbl.Delete( |
| self.cnxn, project_id=789) |
| |
| self.mox.ReplayAll() |
| self.services.issue.ExpungeFormerLocations(self.cnxn, 789) |
| self.mox.VerifyAll() |
| |
| def testExpungeIssues(self): |
| issue_ids = [1, 2] |
| |
| self.mox.StubOutWithMock(search, 'Index') |
| search.Index(name=settings.search_index_name_format % 1).AndReturn( |
| MockIndex()) |
| search.Index(name=settings.search_index_name_format % 2).AndReturn( |
| MockIndex()) |
| |
| self.services.issue.issuesummary_tbl.Delete(self.cnxn, issue_id=[1, 2]) |
| self.services.issue.issue2label_tbl.Delete(self.cnxn, issue_id=[1, 2]) |
| self.services.issue.issue2component_tbl.Delete(self.cnxn, issue_id=[1, 2]) |
| self.services.issue.issue2cc_tbl.Delete(self.cnxn, issue_id=[1, 2]) |
| self.services.issue.issue2notify_tbl.Delete(self.cnxn, issue_id=[1, 2]) |
| self.services.issue.issueupdate_tbl.Delete(self.cnxn, issue_id=[1, 2]) |
| self.services.issue.attachment_tbl.Delete(self.cnxn, issue_id=[1, 2]) |
| self.services.issue.comment_tbl.Delete(self.cnxn, issue_id=[1, 2]) |
| self.services.issue.issuerelation_tbl.Delete(self.cnxn, issue_id=[1, 2]) |
| self.services.issue.issuerelation_tbl.Delete(self.cnxn, dst_issue_id=[1, 2]) |
| self.services.issue.danglingrelation_tbl.Delete(self.cnxn, issue_id=[1, 2]) |
| self.services.issue.issueformerlocations_tbl.Delete( |
| self.cnxn, issue_id=[1, 2]) |
| self.services.issue.reindexqueue_tbl.Delete(self.cnxn, issue_id=[1, 2]) |
| self.services.issue.issue_tbl.Delete(self.cnxn, id=[1, 2]) |
| |
| self.mox.ReplayAll() |
| self.services.issue.ExpungeIssues(self.cnxn, issue_ids) |
| self.mox.VerifyAll() |
| |
| def testSoftDeleteIssue(self): |
| project = fake.Project(project_id=789) |
| issue_1, _issue_2 = self.SetUpGetIssues() |
| self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901) |
| delta = {'deleted': True} |
| self.services.issue.issue_tbl.Update( |
| self.cnxn, delta, id=78901, commit=False) |
| self.cnxn.Commit() |
| self.mox.ReplayAll() |
| self.services.issue.SoftDeleteIssue( |
| self.cnxn, project.project_id, 1, True, self.services.user) |
| self.mox.VerifyAll() |
| self.assertTrue(issue_1.deleted) |
| |
| def SetUpDeleteComponentReferences(self, component_id): |
| self.services.issue.issue2component_tbl.Delete( |
| self.cnxn, component_id=component_id) |
| |
| def testDeleteComponentReferences(self): |
| self.SetUpDeleteComponentReferences(123) |
| self.mox.ReplayAll() |
| self.services.issue.DeleteComponentReferences(self.cnxn, 123) |
| self.mox.VerifyAll() |
| |
| ### Local ID generation |
| |
| def SetUpInitializeLocalID(self, project_id): |
| self.services.issue.localidcounter_tbl.InsertRow( |
| self.cnxn, project_id=project_id, used_local_id=0, used_spam_id=0) |
| |
| def testInitializeLocalID(self): |
| self.SetUpInitializeLocalID(789) |
| self.mox.ReplayAll() |
| self.services.issue.InitializeLocalID(self.cnxn, 789) |
| self.mox.VerifyAll() |
| |
| def SetUpAllocateNextLocalID( |
| self, project_id, highest_in_use, highest_former): |
| highest_either = max(highest_in_use or 0, highest_former or 0) |
| self.services.issue.localidcounter_tbl.IncrementCounterValue( |
| self.cnxn, 'used_local_id', project_id=project_id).AndReturn( |
| highest_either + 1) |
| |
| def SetUpAllocateNextSpamID( |
| self, project_id, highest_in_use, highest_former): |
| highest_either = max(highest_in_use or 0, highest_former or 0) |
| self.services.issue.localidcounter_tbl.IncrementCounterValue( |
| self.cnxn, 'used_spam_id', project_id=project_id).AndReturn( |
| highest_either + 1) |
| |
| def testAllocateNextLocalID_NewProject(self): |
| self.SetUpAllocateNextLocalID(789, None, None) |
| self.mox.ReplayAll() |
| next_local_id = self.services.issue.AllocateNextLocalID(self.cnxn, 789) |
| self.mox.VerifyAll() |
| self.assertEqual(1, next_local_id) |
| |
| def testAllocateNextLocalID_HighestInUse(self): |
| self.SetUpAllocateNextLocalID(789, 14, None) |
| self.mox.ReplayAll() |
| next_local_id = self.services.issue.AllocateNextLocalID(self.cnxn, 789) |
| self.mox.VerifyAll() |
| self.assertEqual(15, next_local_id) |
| |
| def testAllocateNextLocalID_HighestWasMoved(self): |
| self.SetUpAllocateNextLocalID(789, 23, 66) |
| self.mox.ReplayAll() |
| next_local_id = self.services.issue.AllocateNextLocalID(self.cnxn, 789) |
| self.mox.VerifyAll() |
| self.assertEqual(67, next_local_id) |
| |
| def SetUpGetHighestLocalID(self, project_id, highest_in_use, highest_former): |
| self.services.issue.issue_tbl.SelectValue( |
| self.cnxn, 'MAX(local_id)', project_id=project_id).AndReturn( |
| highest_in_use) |
| self.services.issue.issueformerlocations_tbl.SelectValue( |
| self.cnxn, 'MAX(local_id)', project_id=project_id).AndReturn( |
| highest_former) |
| |
| def testGetHighestLocalID_OnlyActiveLocalIDs(self): |
| self.SetUpGetHighestLocalID(789, 14, None) |
| self.mox.ReplayAll() |
| highest_id = self.services.issue.GetHighestLocalID(self.cnxn, 789) |
| self.mox.VerifyAll() |
| self.assertEqual(14, highest_id) |
| |
| def testGetHighestLocalID_OnlyFormerIDs(self): |
| self.SetUpGetHighestLocalID(789, None, 97) |
| self.mox.ReplayAll() |
| highest_id = self.services.issue.GetHighestLocalID(self.cnxn, 789) |
| self.mox.VerifyAll() |
| self.assertEqual(97, highest_id) |
| |
| def testGetHighestLocalID_BothActiveAndFormer(self): |
| self.SetUpGetHighestLocalID(789, 345, 97) |
| self.mox.ReplayAll() |
| highest_id = self.services.issue.GetHighestLocalID(self.cnxn, 789) |
| self.mox.VerifyAll() |
| self.assertEqual(345, highest_id) |
| |
| def testGetAllLocalIDsInProject(self): |
| self.SetUpGetHighestLocalID(789, 14, None) |
| self.mox.ReplayAll() |
| local_id_range = self.services.issue.GetAllLocalIDsInProject(self.cnxn, 789) |
| self.mox.VerifyAll() |
| self.assertEqual(range(1, 15), local_id_range) |
| |
| ### Comments |
| |
| def testDeserializeComments_Empty(self): |
| comments = self.services.issue._DeserializeComments([], [], []) |
| self.assertEqual([], comments) |
| |
| def SetUpCommentRows(self): |
| comment_rows = [ |
| (7890101, 78901, self.now, 789, 111L, |
| 'content', None, True, None, False)] |
| amendment_rows = [ |
| (1, 78901, 7890101, 'cc', 'old', 'new val', 222, None, None)] |
| attachment_rows = [] |
| return comment_rows, amendment_rows, attachment_rows |
| |
| def testDeserializeComments(self): |
| comment_rows, amendment_rows, attachment_rows = self.SetUpCommentRows() |
| comments = self.services.issue._DeserializeComments( |
| comment_rows, amendment_rows, attachment_rows) |
| self.assertEqual(1, len(comments)) |
| |
| def SetUpGetComments(self, issue_ids): |
| # Assumes one comment per issue. |
| cids = [issue_id + 1000 for issue_id in issue_ids] |
| self.services.issue.comment_tbl.Select( |
| self.cnxn, cols=['Comment.id'] + issue_svc.COMMENT_COLS[1:], |
| where=None, issue_id=issue_ids, order_by=[('created', [])]).AndReturn([ |
| (issue_id + 1000, issue_id, self.now, 789, 111L, 'content', |
| None, True, None, False) for issue_id in issue_ids]) |
| # Assume no amendments or attachment for now. |
| self.services.issue.issueupdate_tbl.Select( |
| self.cnxn, cols=issue_svc.ISSUEUPDATE_COLS, |
| comment_id=cids).AndReturn([]) |
| if issue_ids: |
| attachment_rows = [ |
| (1234, issue_ids[0], cids[0], 'a_filename', 1024, 'text/plain', |
| False, None)] |
| else: |
| attachment_rows = [] |
| |
| self.services.issue.attachment_tbl.Select( |
| self.cnxn, cols=issue_svc.ATTACHMENT_COLS, |
| comment_id=cids).AndReturn(attachment_rows) |
| |
| def testGetComments(self): |
| self.SetUpGetComments([100001, 100002]) |
| self.mox.ReplayAll() |
| comments = self.services.issue.GetComments( |
| self.cnxn, issue_id=[100001, 100002]) |
| self.mox.VerifyAll() |
| self.assertEqual(2, len(comments)) |
| self.assertEqual('content', comments[0].content) |
| self.assertEqual('content', comments[1].content) |
| |
| def SetUpGetComment_Found(self, comment_id): |
| # Assumes one comment per issue. |
| self.services.issue.comment_tbl.Select( |
| self.cnxn, cols=['Comment.id'] + issue_svc.COMMENT_COLS[1:], |
| where=None, id=comment_id, order_by=[('created', [])]).AndReturn([ |
| (comment_id, int(comment_id / 100), self.now, 789, 111L, 'content', |
| None, True, None, False)]) |
| # Assume no amendments or attachment for now. |
| self.services.issue.issueupdate_tbl.Select( |
| self.cnxn, cols=issue_svc.ISSUEUPDATE_COLS, |
| comment_id=[comment_id]).AndReturn([]) |
| self.services.issue.attachment_tbl.Select( |
| self.cnxn, cols=issue_svc.ATTACHMENT_COLS, |
| comment_id=[comment_id]).AndReturn([]) |
| |
| def testGetComment_Found(self): |
| self.SetUpGetComment_Found(7890101) |
| self.mox.ReplayAll() |
| comment = self.services.issue.GetComment(self.cnxn, 7890101) |
| self.mox.VerifyAll() |
| self.assertEqual('content', comment.content) |
| |
| def SetUpGetComment_Missing(self, comment_id): |
| # Assumes one comment per issue. |
| self.services.issue.comment_tbl.Select( |
| self.cnxn, cols=['Comment.id'] + issue_svc.COMMENT_COLS[1:], |
| where=None, id=comment_id, order_by=[('created', [])]).AndReturn([]) |
| # Assume no amendments or attachment for now. |
| self.services.issue.issueupdate_tbl.Select( |
| self.cnxn, cols=issue_svc.ISSUEUPDATE_COLS, |
| comment_id=[]).AndReturn([]) |
| self.services.issue.attachment_tbl.Select( |
| self.cnxn, cols=issue_svc.ATTACHMENT_COLS, comment_id=[]).AndReturn([]) |
| |
| def testGetComment_Missing(self): |
| self.SetUpGetComment_Missing(7890101) |
| self.mox.ReplayAll() |
| self.assertRaises( |
| issue_svc.NoSuchCommentException, |
| self.services.issue.GetComment, self.cnxn, 7890101) |
| self.mox.VerifyAll() |
| |
| def testGetCommentsForIssue(self): |
| issue = fake.MakeTestIssue(789, 1, 'Summary', 'New', 111L) |
| self.SetUpGetComments([issue.issue_id]) |
| self.mox.ReplayAll() |
| self.services.issue.GetCommentsForIssue(self.cnxn, issue.issue_id) |
| self.mox.VerifyAll() |
| |
| def testGetCommentsForIssues(self): |
| self.SetUpGetComments([100001, 100002]) |
| self.mox.ReplayAll() |
| self.services.issue.GetCommentsForIssues( |
| self.cnxn, issue_ids=[100001, 100002]) |
| self.mox.VerifyAll() |
| |
| def SetUpInsertComment(self, comment_id, was_escaped, is_spam=False): |
| self.services.issue.comment_tbl.InsertRow( |
| self.cnxn, issue_id=78901, created=self.now, project_id=789, |
| commenter_id=111L, content='content', inbound_message=None, |
| was_escaped=was_escaped, deleted_by=None, is_spam=is_spam, |
| commit=True).AndReturn(comment_id) |
| |
| amendment_rows = [] |
| self.services.issue.issueupdate_tbl.InsertRows( |
| self.cnxn, issue_svc.ISSUEUPDATE_COLS[1:], amendment_rows, |
| commit=True) |
| |
| attachment_rows = [] |
| self.services.issue.attachment_tbl.InsertRows( |
| self.cnxn, issue_svc.ATTACHMENT_COLS[1:], attachment_rows, |
| commit=True) |
| |
| def testInsertComment(self): |
| self.SetUpInsertComment(7890101, False) |
| self.mox.ReplayAll() |
| comment = tracker_pb2.IssueComment( |
| issue_id=78901, timestamp=self.now, project_id=789, user_id=111L, |
| content='content', was_escaped=False) |
| self.services.issue.InsertComment(self.cnxn, comment, commit=True) |
| self.mox.VerifyAll() |
| self.assertEqual(7890101, comment.id) |
| |
| def SetUpUpdateComment(self, comment_id, delta=None): |
| delta = delta or { |
| 'commenter_id': 111L, |
| 'content': 'new content', |
| 'deleted_by': 222L, |
| 'is_spam': False, |
| } |
| self.services.issue.comment_tbl.Update( |
| self.cnxn, delta, id=comment_id) |
| |
| def testUpdateComment(self): |
| self.SetUpUpdateComment(7890101) |
| self.mox.ReplayAll() |
| comment = tracker_pb2.IssueComment( |
| id=7890101, issue_id=78901, timestamp=self.now, project_id=789, |
| user_id=111L, content='new content', was_escaped=True, deleted_by=222L, |
| is_spam=False) |
| self.services.issue._UpdateComment(self.cnxn, comment) |
| self.mox.VerifyAll() |
| |
| def testMakeIssueComment(self): |
| comment = self.services.issue._MakeIssueComment( |
| 789, 111L, 'content', timestamp=self.now) |
| self.assertEqual('content', comment.content) |
| self.assertEqual([], comment.amendments) |
| self.assertEqual([], comment.attachments) |
| |
| def testCreateIssueComment(self): |
| _issue_1, _issue_2 = self.SetUpGetIssues() |
| self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901) |
| self.SetUpInsertComment(7890101, False) |
| self.mox.ReplayAll() |
| comment = self.services.issue.CreateIssueComment( |
| self.cnxn, 789, 1, 111L, 'content', timestamp=self.now) |
| self.mox.VerifyAll() |
| self.assertEqual('content', comment.content) |
| |
| def testCreateIssueComment_spam(self): |
| _issue_1, _issue_2 = self.SetUpGetIssues() |
| self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901) |
| self.SetUpInsertComment(7890101, False, is_spam=True) |
| self.mox.ReplayAll() |
| comment = self.services.issue.CreateIssueComment( |
| self.cnxn, 789, 1, 111L, 'content', timestamp=self.now, is_spam=True) |
| self.mox.VerifyAll() |
| self.assertEqual('content', comment.content) |
| self.assertTrue(comment.is_spam) |
| |
| def testSoftDeleteComment(self): |
| issue_1, _issue_2 = self.SetUpGetIssues() |
| issue_1.attachment_count = 1 |
| self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901) |
| self.SetUpGetComments([78901]) |
| self.SetUpUpdateComment(79901, delta={'deleted_by': 222L, 'is_spam': False}) |
| self.SetUpUpdateIssues(given_delta={'attachment_count': 0}) |
| self.mox.ReplayAll() |
| self.services.issue.SoftDeleteComment( |
| self.cnxn, 789, 1, 0, 222L, self.services.user) |
| self.mox.VerifyAll() |
| |
| ### Attachments |
| |
| def testGetAttachmentAndContext(self): |
| # TODO(jrobbins): re-implemnent to use Google Cloud Storage. |
| pass |
| |
| def SetUpUpdateAttachment(self, attachment_id, delta): |
| self.services.issue.attachment_tbl.Update( |
| self.cnxn, delta, id=attachment_id) |
| |
| def testUpdateAttachment(self): |
| delta = { |
| 'filename': 'a_filename', |
| 'filesize': 1024, |
| 'mimetype': 'text/plain', |
| 'deleted': False, |
| } |
| self.SetUpUpdateAttachment(1234, delta) |
| self.mox.ReplayAll() |
| attach = tracker_pb2.Attachment( |
| attachment_id=1234, filename='a_filename', filesize=1024, |
| mimetype='text/plain') |
| self.services.issue._UpdateAttachment(self.cnxn, attach) |
| self.mox.VerifyAll() |
| |
| def testStoreAttachmentBlob(self): |
| # TODO(jrobbins): re-implemnent to use Google Cloud Storage. |
| pass |
| |
| def testSoftDeleteAttachment(self): |
| issue_1, _issue_2 = self.SetUpGetIssues() |
| issue_1.attachment_count = 1 |
| self.services.issue.issue_id_2lc.CacheItem((789, 1), 78901) |
| self.SetUpGetComments([78901]) |
| self.SetUpUpdateAttachment(1234, {'deleted': True}) |
| self.SetUpUpdateIssues(given_delta={'attachment_count': 0}) |
| |
| self.mox.ReplayAll() |
| self.services.issue.SoftDeleteAttachment( |
| self.cnxn, 789, 1, 0, 1234, self.services.user) |
| self.mox.VerifyAll() |
| |
| ### Reindex queue |
| |
| def SetUpEnqueueIssuesForIndexing(self, issue_ids): |
| reindex_rows = [(issue_id,) for issue_id in issue_ids] |
| self.services.issue.reindexqueue_tbl.InsertRows( |
| self.cnxn, ['issue_id'], reindex_rows, ignore=True) |
| |
| def testEnqueueIssuesForIndexing(self): |
| self.SetUpEnqueueIssuesForIndexing([78901]) |
| self.mox.ReplayAll() |
| self.services.issue.EnqueueIssuesForIndexing(self.cnxn, [78901]) |
| self.mox.VerifyAll() |
| |
| def SetUpReindexIssues(self, issue_ids): |
| self.services.issue.reindexqueue_tbl.Select( |
| self.cnxn, order_by=[('created', [])], |
| limit=50).AndReturn([(issue_id,) for issue_id in issue_ids]) |
| |
| if issue_ids: |
| _issue_1, _issue_2 = self.SetUpGetIssues() |
| self.services.issue.reindexqueue_tbl.Delete( |
| self.cnxn, issue_id=issue_ids) |
| |
| def testReindexIssues_QueueEmpty(self): |
| self.SetUpReindexIssues([]) |
| self.mox.ReplayAll() |
| self.services.issue.ReindexIssues(self.cnxn, 50, self.services.user) |
| self.mox.VerifyAll() |
| |
| def testReindexIssues_QueueHasTwoIssues(self): |
| self.SetUpReindexIssues([78901, 78902]) |
| self.mox.ReplayAll() |
| self.services.issue.ReindexIssues(self.cnxn, 50, self.services.user) |
| self.mox.VerifyAll() |
| |
| ### Search functions |
| |
| def SetUpRunIssueQuery( |
| self, rows, limit=settings.search_limit_per_shard): |
| self.services.issue.issue_tbl.Select( |
| self.cnxn, shard_id=1, distinct=True, cols=['Issue.id'], |
| left_joins=[], where=[('Issue.deleted = %s', [False])], order_by=[], |
| limit=limit).AndReturn(rows) |
| |
| def testRunIssueQuery_NoResults(self): |
| self.SetUpRunIssueQuery([]) |
| self.mox.ReplayAll() |
| result_iids, capped = self.services.issue.RunIssueQuery( |
| self.cnxn, [], [], [], shard_id=1) |
| self.mox.VerifyAll() |
| self.assertEqual([], result_iids) |
| self.assertFalse(capped) |
| |
| def testRunIssueQuery_Normal(self): |
| self.SetUpRunIssueQuery([(1,), (11,), (21,)]) |
| self.mox.ReplayAll() |
| result_iids, capped = self.services.issue.RunIssueQuery( |
| self.cnxn, [], [], [], shard_id=1) |
| self.mox.VerifyAll() |
| self.assertEqual([1, 11, 21], result_iids) |
| self.assertFalse(capped) |
| |
| def testRunIssueQuery_Capped(self): |
| try: |
| orig = settings.search_limit_per_shard |
| settings.search_limit_per_shard = 3 |
| self.SetUpRunIssueQuery([(1,), (11,), (21,)], limit=3) |
| self.mox.ReplayAll() |
| result_iids, capped = self.services.issue.RunIssueQuery( |
| self.cnxn, [], [], [], shard_id=1) |
| self.mox.VerifyAll() |
| self.assertEqual([1, 11, 21], result_iids) |
| self.assertTrue(capped) |
| finally: |
| settings.search_limit_per_shard = orig |
| |
| def SetUpGetIIDsByLabelIDs(self): |
| self.services.issue.issue_tbl.Select( |
| self.cnxn, shard_id=1, cols=['id'], |
| left_joins=[('Issue2Label ON Issue.id = Issue2Label.issue_id', [])], |
| label_id=[123, 456], project_id=789, |
| where=[('shard = %s', [1])] |
| ).AndReturn([(1,), (2,), (3,)]) |
| |
| def testGetIIDsByLabelIDs(self): |
| self.SetUpGetIIDsByLabelIDs() |
| self.mox.ReplayAll() |
| iids = self.services.issue.GetIIDsByLabelIDs(self.cnxn, [123, 456], 789, 1) |
| self.mox.VerifyAll() |
| self.assertEqual([1, 2, 3], iids) |
| |
| def SetUpGetIIDsByParticipant(self): |
| self.services.issue.issue_tbl.Select( |
| self.cnxn, shard_id=1, cols=['id'], |
| reporter_id=[111L, 888L], |
| where=[('shard = %s', [1]), ('Issue.project_id IN (%s)', [789])] |
| ).AndReturn([(1,)]) |
| self.services.issue.issue_tbl.Select( |
| self.cnxn, shard_id=1, cols=['id'], |
| owner_id=[111L, 888L], |
| where=[('shard = %s', [1]), ('Issue.project_id IN (%s)', [789])] |
| ).AndReturn([(2,)]) |
| self.services.issue.issue_tbl.Select( |
| self.cnxn, shard_id=1, cols=['id'], |
| derived_owner_id=[111L, 888L], |
| where=[('shard = %s', [1]), ('Issue.project_id IN (%s)', [789])] |
| ).AndReturn([(3,)]) |
| self.services.issue.issue_tbl.Select( |
| self.cnxn, shard_id=1, cols=['id'], |
| left_joins=[('Issue2Cc ON Issue2Cc.issue_id = Issue.id', [])], |
| cc_id=[111L, 888L], |
| where=[('shard = %s', [1]), ('Issue.project_id IN (%s)', [789]), |
| ('cc_id IS NOT NULL', [])] |
| ).AndReturn([(4,)]) |
| self.services.issue.issue_tbl.Select( |
| self.cnxn, shard_id=1, cols=['Issue.id'], |
| left_joins=[ |
| ('Issue2FieldValue ON Issue.id = Issue2FieldValue.issue_id', []), |
| ('FieldDef ON Issue2FieldValue.field_id = FieldDef.id', [])], |
| user_id=[111L, 888L], grants_perm='View', |
| where=[('shard = %s', [1]), ('Issue.project_id IN (%s)', [789]), |
| ('user_id IS NOT NULL', [])] |
| ).AndReturn([(5,)]) |
| |
| def testGetIIDsByParticipant(self): |
| self.SetUpGetIIDsByParticipant() |
| self.mox.ReplayAll() |
| iids = self.services.issue.GetIIDsByParticipant( |
| self.cnxn, [111L, 888L], [789], 1) |
| self.mox.VerifyAll() |
| self.assertEqual([1, 2, 3, 4, 5], iids) |
| |
| |
| class IssueServiceFunctionsTest(unittest.TestCase): |
| |
| def testUpdateClosedTimestamp(self): |
| config = tracker_pb2.ProjectIssueConfig() |
| config.well_known_statuses.append(tracker_pb2.StatusDef( |
| status='New', means_open=True)) |
| config.well_known_statuses.append(tracker_pb2.StatusDef( |
| status='Accepted', means_open=True)) |
| config.well_known_statuses.append(tracker_pb2.StatusDef( |
| status='Old', means_open=False)) |
| config.well_known_statuses.append(tracker_pb2.StatusDef( |
| status='Closed', means_open=False)) |
| |
| issue = tracker_pb2.Issue() |
| issue.local_id = 1234 |
| issue.status = 'New' |
| |
| # ensure the default value is undef |
| self.assert_(not issue.closed_timestamp) |
| |
| # ensure transitioning to the same and other open states |
| # doesn't set the timestamp |
| issue.status = 'New' |
| issue_svc._UpdateClosedTimestamp(config, issue, 'New') |
| self.assert_(not issue.closed_timestamp) |
| |
| issue.status = 'Accepted' |
| issue_svc._UpdateClosedTimestamp(config, issue, 'New') |
| self.assert_(not issue.closed_timestamp) |
| |
| # ensure transitioning from open to closed sets the timestamp |
| issue.status = 'Closed' |
| issue_svc._UpdateClosedTimestamp(config, issue, 'Accepted') |
| self.assert_(issue.closed_timestamp) |
| |
| # ensure that the timestamp is cleared when transitioning from |
| # closed to open |
| issue.status = 'New' |
| issue_svc._UpdateClosedTimestamp(config, issue, 'Closed') |
| self.assert_(not issue.closed_timestamp) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |