blob: a43ffb15911b4f8d4174b37f8c5623c5b18850fb [file] [log] [blame]
# 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()