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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: appengine/monorail/services/test/issue_svc_test.py
diff --git a/appengine/monorail/services/test/issue_svc_test.py b/appengine/monorail/services/test/issue_svc_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..a43ffb15911b4f8d4174b37f8c5623c5b18850fb
--- /dev/null
+++ b/appengine/monorail/services/test/issue_svc_test.py
@@ -0,0 +1,1469 @@
+# 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()
« 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