blob: 6d1d94150f4c1d936f65025358bd7e28b1c310df [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001# Copyright 2016 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file or at
4# https://developers.google.com/open-source/licenses/bsd
5
6"""Unit tests for config_svc module."""
7from __future__ import print_function
8from __future__ import division
9from __future__ import absolute_import
10
11import re
12import unittest
13import logging
14import mock
15
16import mox
17
18from google.appengine.api import memcache
19from google.appengine.ext import testbed
20
21from framework import exceptions
22from framework import framework_constants
23from framework import sql
24from proto import tracker_pb2
25from services import config_svc
26from services import template_svc
27from testing import fake
28from tracker import tracker_bizobj
29from tracker import tracker_constants
30
31LABEL_ROW_SHARDS = config_svc.LABEL_ROW_SHARDS
32
33
34def MakeConfigService(cache_manager, my_mox):
35 config_service = config_svc.ConfigService(cache_manager)
36 for table_var in ['projectissueconfig_tbl', 'statusdef_tbl', 'labeldef_tbl',
37 'fielddef_tbl', 'fielddef2admin_tbl', 'fielddef2editor_tbl',
38 'componentdef_tbl', 'component2admin_tbl',
39 'component2cc_tbl', 'component2label_tbl',
40 'approvaldef2approver_tbl', 'approvaldef2survey_tbl']:
41 setattr(config_service, table_var, my_mox.CreateMock(sql.SQLTableManager))
42
43 return config_service
44
45
46class LabelRowTwoLevelCacheTest(unittest.TestCase):
47
48 def setUp(self):
49 self.mox = mox.Mox()
50 self.cnxn = 'fake connection'
51 self.cache_manager = fake.CacheManager()
52 self.config_service = MakeConfigService(self.cache_manager, self.mox)
53 self.label_row_2lc = self.config_service.label_row_2lc
54
55 self.rows = [(1, 789, 1, 'A', 'doc', False),
56 (2, 789, 2, 'B', 'doc', False),
57 (3, 678, 1, 'C', 'doc', True),
58 (4, 678, None, 'D', 'doc', False)]
59
60 def tearDown(self):
61 self.mox.UnsetStubs()
62 self.mox.ResetAll()
63
64 def testDeserializeLabelRows_Empty(self):
65 label_row_dict = self.label_row_2lc._DeserializeLabelRows([])
66 self.assertEqual({}, label_row_dict)
67
68 def testDeserializeLabelRows_Normal(self):
69 label_rows_dict = self.label_row_2lc._DeserializeLabelRows(self.rows)
70 expected = {
71 (789, 1): [(1, 789, 1, 'A', 'doc', False)],
72 (789, 2): [(2, 789, 2, 'B', 'doc', False)],
73 (678, 3): [(3, 678, 1, 'C', 'doc', True)],
74 (678, 4): [(4, 678, None, 'D', 'doc', False)],
75 }
76 self.assertEqual(expected, label_rows_dict)
77
78 def SetUpFetchItems(self, keys, rows):
79 for (project_id, shard_id) in keys:
80 sharded_rows = [row for row in rows
81 if row[0] % LABEL_ROW_SHARDS == shard_id]
82 self.config_service.labeldef_tbl.Select(
83 self.cnxn, cols=config_svc.LABELDEF_COLS, project_id=project_id,
84 where=[('id %% %s = %s', [LABEL_ROW_SHARDS, shard_id])]).AndReturn(
85 sharded_rows)
86
87 def testFetchItems(self):
88 keys = [(567, 0), (678, 0), (789, 0),
89 (567, 1), (678, 1), (789, 1),
90 (567, 2), (678, 2), (789, 2),
91 (567, 3), (678, 3), (789, 3),
92 (567, 4), (678, 4), (789, 4),
93 ]
94 self.SetUpFetchItems(keys, self.rows)
95 self.mox.ReplayAll()
96 label_rows_dict = self.label_row_2lc.FetchItems(self.cnxn, keys)
97 self.mox.VerifyAll()
98 expected = {
99 (567, 0): [],
100 (678, 0): [],
101 (789, 0): [],
102 (567, 1): [],
103 (678, 1): [],
104 (789, 1): [(1, 789, 1, 'A', 'doc', False)],
105 (567, 2): [],
106 (678, 2): [],
107 (789, 2): [(2, 789, 2, 'B', 'doc', False)],
108 (567, 3): [],
109 (678, 3): [(3, 678, 1, 'C', 'doc', True)],
110 (789, 3): [],
111 (567, 4): [],
112 (678, 4): [(4, 678, None, 'D', 'doc', False)],
113 (789, 4): [],
114 }
115 self.assertEqual(expected, label_rows_dict)
116
117
118class StatusRowTwoLevelCacheTest(unittest.TestCase):
119
120 def setUp(self):
121 self.mox = mox.Mox()
122 self.cnxn = 'fake connection'
123 self.cache_manager = fake.CacheManager()
124 self.config_service = MakeConfigService(self.cache_manager, self.mox)
125 self.status_row_2lc = self.config_service.status_row_2lc
126
127 self.rows = [(1, 789, 1, 'A', True, 'doc', False),
128 (2, 789, 2, 'B', False, 'doc', False),
129 (3, 678, 1, 'C', True, 'doc', True),
130 (4, 678, None, 'D', True, 'doc', False)]
131
132 def tearDown(self):
133 self.mox.UnsetStubs()
134 self.mox.ResetAll()
135
136 def testDeserializeStatusRows_Empty(self):
137 status_row_dict = self.status_row_2lc._DeserializeStatusRows([])
138 self.assertEqual({}, status_row_dict)
139
140 def testDeserializeStatusRows_Normal(self):
141 status_rows_dict = self.status_row_2lc._DeserializeStatusRows(self.rows)
142 expected = {
143 678: [(3, 678, 1, 'C', True, 'doc', True),
144 (4, 678, None, 'D', True, 'doc', False)],
145 789: [(1, 789, 1, 'A', True, 'doc', False),
146 (2, 789, 2, 'B', False, 'doc', False)],
147 }
148 self.assertEqual(expected, status_rows_dict)
149
150 def SetUpFetchItems(self, keys, rows):
151 self.config_service.statusdef_tbl.Select(
152 self.cnxn, cols=config_svc.STATUSDEF_COLS, project_id=keys,
153 order_by=[('rank DESC', []), ('status DESC', [])]).AndReturn(
154 rows)
155
156 def testFetchItems(self):
157 keys = [567, 678, 789]
158 self.SetUpFetchItems(keys, self.rows)
159 self.mox.ReplayAll()
160 status_rows_dict = self.status_row_2lc.FetchItems(self.cnxn, keys)
161 self.mox.VerifyAll()
162 expected = {
163 567: [],
164 678: [(3, 678, 1, 'C', True, 'doc', True),
165 (4, 678, None, 'D', True, 'doc', False)],
166 789: [(1, 789, 1, 'A', True, 'doc', False),
167 (2, 789, 2, 'B', False, 'doc', False)],
168 }
169 self.assertEqual(expected, status_rows_dict)
170
171
172class ConfigRowTwoLevelCacheTest(unittest.TestCase):
173
174 def setUp(self):
175 self.mox = mox.Mox()
176 self.cnxn = 'fake connection'
177 self.cache_manager = fake.CacheManager()
178 self.config_service = MakeConfigService(self.cache_manager, self.mox)
179 self.config_2lc = self.config_service.config_2lc
180
181 self.config_rows = [
182 (789, 'Duplicate', 'Pri Type', 1, 2,
183 'Type Pri Summary', '-Pri', 'Mstone', 'Owner',
184 '', None)]
185 self.statusdef_rows = [(1, 789, 1, 'New', True, 'doc', False),
186 (2, 789, 2, 'Fixed', False, 'doc', False)]
187 self.labeldef_rows = [(1, 789, 1, 'Security', 'doc', False),
188 (2, 789, 2, 'UX', 'doc', False)]
189 self.fielddef_rows = [
190 (
191 1, 789, None, 'Field', 'INT_TYPE', 'Defect', '', False, False,
192 False, 1, 99, None, '', '', None, 'NEVER', 'no_action', 'doc',
193 False, None, False, False)
194 ]
195 self.approvaldef2approver_rows = [(2, 101, 789), (2, 102, 789)]
196 self.approvaldef2survey_rows = [(2, 'Q1\nQ2\nQ3', 789)]
197 self.fielddef2admin_rows = [(1, 111), (1, 222)]
198 self.fielddef2editor_rows = [(1, 111), (1, 222), (1, 333)]
199 self.componentdef_rows = []
200 self.component2admin_rows = []
201 self.component2cc_rows = []
202 self.component2label_rows = []
203
204 def tearDown(self):
205 self.mox.UnsetStubs()
206 self.mox.ResetAll()
207
208 def testDeserializeIssueConfigs_Empty(self):
209 config_dict = self.config_2lc._DeserializeIssueConfigs(
210 [], [], [], [], [], [], [], [], [], [], [], [])
211 self.assertEqual({}, config_dict)
212
213 def testDeserializeIssueConfigs_Normal(self):
214 config_dict = self.config_2lc._DeserializeIssueConfigs(
215 self.config_rows, self.statusdef_rows, self.labeldef_rows,
216 self.fielddef_rows, self.fielddef2admin_rows, self.fielddef2editor_rows,
217 self.componentdef_rows, self.component2admin_rows,
218 self.component2cc_rows, self.component2label_rows,
219 self.approvaldef2approver_rows, self.approvaldef2survey_rows)
220 self.assertItemsEqual([789], list(config_dict.keys()))
221 config = config_dict[789]
222 self.assertEqual(789, config.project_id)
223 self.assertEqual(['Duplicate'], config.statuses_offer_merge)
224 self.assertEqual(len(self.labeldef_rows), len(config.well_known_labels))
225 self.assertEqual(len(self.statusdef_rows), len(config.well_known_statuses))
226 self.assertEqual(len(self.fielddef_rows), len(config.field_defs))
227 self.assertEqual(len(self.componentdef_rows), len(config.component_defs))
228 self.assertEqual(
229 len(self.fielddef2admin_rows), len(config.field_defs[0].admin_ids))
230 self.assertEqual(
231 len(self.fielddef2editor_rows), len(config.field_defs[0].editor_ids))
232 self.assertEqual(len(self.approvaldef2approver_rows),
233 len(config.approval_defs[0].approver_ids))
234 self.assertEqual(config.approval_defs[0].survey, 'Q1\nQ2\nQ3')
235
236 def SetUpFetchConfigs(self, project_ids):
237 self.config_service.projectissueconfig_tbl.Select(
238 self.cnxn, cols=config_svc.PROJECTISSUECONFIG_COLS,
239 project_id=project_ids).AndReturn(self.config_rows)
240
241 self.config_service.statusdef_tbl.Select(
242 self.cnxn, cols=config_svc.STATUSDEF_COLS, project_id=project_ids,
243 where=[('rank IS NOT NULL', [])], order_by=[('rank', [])]).AndReturn(
244 self.statusdef_rows)
245
246 self.config_service.labeldef_tbl.Select(
247 self.cnxn, cols=config_svc.LABELDEF_COLS, project_id=project_ids,
248 where=[('rank IS NOT NULL', [])], order_by=[('rank', [])]).AndReturn(
249 self.labeldef_rows)
250
251 self.config_service.approvaldef2approver_tbl.Select(
252 self.cnxn, cols=config_svc.APPROVALDEF2APPROVER_COLS,
253 project_id=project_ids).AndReturn(self.approvaldef2approver_rows)
254 self.config_service.approvaldef2survey_tbl.Select(
255 self.cnxn, cols=config_svc.APPROVALDEF2SURVEY_COLS,
256 project_id=project_ids).AndReturn(self.approvaldef2survey_rows)
257
258 self.config_service.fielddef_tbl.Select(
259 self.cnxn, cols=config_svc.FIELDDEF_COLS, project_id=project_ids,
260 order_by=[('field_name', [])]).AndReturn(self.fielddef_rows)
261 field_ids = [row[0] for row in self.fielddef_rows]
262 self.config_service.fielddef2admin_tbl.Select(
263 self.cnxn, cols=config_svc.FIELDDEF2ADMIN_COLS,
264 field_id=field_ids).AndReturn(self.fielddef2admin_rows)
265 self.config_service.fielddef2editor_tbl.Select(
266 self.cnxn, cols=config_svc.FIELDDEF2EDITOR_COLS,
267 field_id=field_ids).AndReturn(self.fielddef2editor_rows)
268
269 self.config_service.componentdef_tbl.Select(
270 self.cnxn, cols=config_svc.COMPONENTDEF_COLS, project_id=project_ids,
271 is_deleted=False,
272 order_by=[('path', [])]).AndReturn(self.componentdef_rows)
273
274 def testFetchConfigs(self):
275 keys = [789]
276 self.SetUpFetchConfigs(keys)
277 self.mox.ReplayAll()
278 config_dict = self.config_2lc._FetchConfigs(self.cnxn, keys)
279 self.mox.VerifyAll()
280 self.assertItemsEqual(keys, list(config_dict.keys()))
281
282 def testFetchItems(self):
283 keys = [678, 789]
284 self.SetUpFetchConfigs(keys)
285 self.mox.ReplayAll()
286 config_dict = self.config_2lc.FetchItems(self.cnxn, keys)
287 self.mox.VerifyAll()
288 self.assertItemsEqual(keys, list(config_dict.keys()))
289
290
291class ConfigServiceTest(unittest.TestCase):
292
293 def setUp(self):
294 self.testbed = testbed.Testbed()
295 self.testbed.activate()
296 self.testbed.init_memcache_stub()
297
298 self.mox = mox.Mox()
299 self.cnxn = self.mox.CreateMock(sql.MonorailConnection)
300 self.cache_manager = fake.CacheManager()
301 self.config_service = MakeConfigService(self.cache_manager, self.mox)
302
303 def tearDown(self):
304 self.testbed.deactivate()
305 self.mox.UnsetStubs()
306 self.mox.ResetAll()
307
308 ### Label lookups
309
310 def testGetLabelDefRows_Hit(self):
311 self.config_service.label_row_2lc.CacheItem((789, 0), [])
312 self.config_service.label_row_2lc.CacheItem((789, 1), [])
313 self.config_service.label_row_2lc.CacheItem((789, 2), [])
314 self.config_service.label_row_2lc.CacheItem(
315 (789, 3), [(3, 678, 1, 'C', 'doc', True)])
316 self.config_service.label_row_2lc.CacheItem(
317 (789, 4), [(4, 678, None, 'D', 'doc', False)])
318 self.config_service.label_row_2lc.CacheItem((789, 5), [])
319 self.config_service.label_row_2lc.CacheItem((789, 6), [])
320 self.config_service.label_row_2lc.CacheItem((789, 7), [])
321 self.config_service.label_row_2lc.CacheItem((789, 8), [])
322 self.config_service.label_row_2lc.CacheItem((789, 9), [])
323 actual = self.config_service.GetLabelDefRows(self.cnxn, 789)
324 expected = [
325 (3, 678, 1, 'C', 'doc', True),
326 (4, 678, None, 'D', 'doc', False)]
327 self.assertEqual(expected, actual)
328
329 def SetUpGetLabelDefRowsAnyProject(self, rows):
330 self.config_service.labeldef_tbl.Select(
331 self.cnxn, cols=config_svc.LABELDEF_COLS, where=None,
332 order_by=[('rank DESC', []), ('label DESC', [])]).AndReturn(
333 rows)
334
335 def testGetLabelDefRowsAnyProject(self):
336 rows = 'foo'
337 self.SetUpGetLabelDefRowsAnyProject(rows)
338 self.mox.ReplayAll()
339 actual = self.config_service.GetLabelDefRowsAnyProject(self.cnxn)
340 self.mox.VerifyAll()
341 self.assertEqual(rows, actual)
342
343 def testDeserializeLabels(self):
344 labeldef_rows = [(1, 789, 1, 'Security', 'doc', False),
345 (2, 789, 2, 'UX', 'doc', True)]
346 id_to_name, name_to_id = self.config_service._DeserializeLabels(
347 labeldef_rows)
348 self.assertEqual({1: 'Security', 2: 'UX'}, id_to_name)
349 self.assertEqual({'security': 1, 'ux': 2}, name_to_id)
350
351 def testEnsureLabelCacheEntry_Hit(self):
352 label_dicts = 'foo'
353 self.config_service.label_cache.CacheItem(789, label_dicts)
354 # No mock calls set up because none are needed.
355 self.mox.ReplayAll()
356 self.config_service._EnsureLabelCacheEntry(self.cnxn, 789)
357 self.mox.VerifyAll()
358
359 def SetUpEnsureLabelCacheEntry_Miss(self, project_id, rows):
360 for shard_id in range(0, LABEL_ROW_SHARDS):
361 shard_rows = [row for row in rows
362 if row[0] % LABEL_ROW_SHARDS == shard_id]
363 self.config_service.labeldef_tbl.Select(
364 self.cnxn, cols=config_svc.LABELDEF_COLS, project_id=project_id,
365 where=[('id %% %s = %s', [LABEL_ROW_SHARDS, shard_id])]).AndReturn(
366 shard_rows)
367
368 def testEnsureLabelCacheEntry_Miss(self):
369 labeldef_rows = [(1, 789, 1, 'Security', 'doc', False),
370 (2, 789, 2, 'UX', 'doc', True)]
371 self.SetUpEnsureLabelCacheEntry_Miss(789, labeldef_rows)
372 self.mox.ReplayAll()
373 self.config_service._EnsureLabelCacheEntry(self.cnxn, 789)
374 self.mox.VerifyAll()
375 label_dicts = {1: 'Security', 2: 'UX'}, {'security': 1, 'ux': 2}
376 self.assertEqual(label_dicts, self.config_service.label_cache.GetItem(789))
377
378 def testLookupLabel_Hit(self):
379 label_dicts = {1: 'Security', 2: 'UX'}, {'security': 1, 'ux': 2}
380 self.config_service.label_cache.CacheItem(789, label_dicts)
381 # No mock calls set up because none are needed.
382 self.mox.ReplayAll()
383 self.assertEqual(
384 'Security', self.config_service.LookupLabel(self.cnxn, 789, 1))
385 self.assertEqual(
386 'UX', self.config_service.LookupLabel(self.cnxn, 789, 2))
387 self.mox.VerifyAll()
388
389 def testLookupLabelID_Hit(self):
390 label_dicts = {1: 'Security', 2: 'UX'}, {'security': 1, 'ux': 2}
391 self.config_service.label_cache.CacheItem(789, label_dicts)
392 # No mock calls set up because none are needed.
393 self.mox.ReplayAll()
394 self.assertEqual(
395 1, self.config_service.LookupLabelID(self.cnxn, 789, 'Security'))
396 self.assertEqual(
397 2, self.config_service.LookupLabelID(self.cnxn, 789, 'UX'))
398 self.mox.VerifyAll()
399
400 def testLookupLabelID_MissAndDoubleCheck(self):
401 label_dicts = {1: 'Security', 2: 'UX'}, {'security': 1, 'ux': 2}
402 self.config_service.label_cache.CacheItem(789, label_dicts)
403
404 self.config_service.labeldef_tbl.Select(
405 self.cnxn, cols=['id'], project_id=789,
406 where=[('LOWER(label) = %s', ['newlabel'])],
407 limit=1).AndReturn([(3,)])
408 self.mox.ReplayAll()
409 self.assertEqual(
410 3, self.config_service.LookupLabelID(self.cnxn, 789, 'NewLabel'))
411 self.mox.VerifyAll()
412
413 def testLookupLabelID_MissAutocreate(self):
414 label_dicts = {1: 'Security', 2: 'UX'}, {'security': 1, 'ux': 2}
415 self.config_service.label_cache.CacheItem(789, label_dicts)
416
417 self.config_service.labeldef_tbl.Select(
418 self.cnxn, cols=['id'], project_id=789,
419 where=[('LOWER(label) = %s', ['newlabel'])],
420 limit=1).AndReturn([])
421 self.config_service.labeldef_tbl.InsertRow(
422 self.cnxn, project_id=789, label='NewLabel').AndReturn(3)
423 self.mox.ReplayAll()
424 self.assertEqual(
425 3, self.config_service.LookupLabelID(self.cnxn, 789, 'NewLabel'))
426 self.mox.VerifyAll()
427
428 def testLookupLabelID_MissDontAutocreate(self):
429 label_dicts = {1: 'Security', 2: 'UX'}, {'security': 1, 'ux': 2}
430 self.config_service.label_cache.CacheItem(789, label_dicts)
431
432 self.config_service.labeldef_tbl.Select(
433 self.cnxn, cols=['id'], project_id=789,
434 where=[('LOWER(label) = %s', ['newlabel'])],
435 limit=1).AndReturn([])
436 self.mox.ReplayAll()
437 self.assertIsNone(self.config_service.LookupLabelID(
438 self.cnxn, 789, 'NewLabel', autocreate=False))
439 self.mox.VerifyAll()
440
441 def testLookupLabelIDs_Hit(self):
442 label_dicts = {1: 'Security', 2: 'UX'}, {'security': 1, 'ux': 2}
443 self.config_service.label_cache.CacheItem(789, label_dicts)
444 # No mock calls set up because none are needed.
445 self.mox.ReplayAll()
446 self.assertEqual(
447 [1, 2],
448 self.config_service.LookupLabelIDs(self.cnxn, 789, ['Security', 'UX']))
449 self.mox.VerifyAll()
450
451 def testLookupIDsOfLabelsMatching_Hit(self):
452 label_dicts = {1: 'Security', 2: 'UX'}, {'security': 1, 'ux': 2}
453 self.config_service.label_cache.CacheItem(789, label_dicts)
454 # No mock calls set up because none are needed.
455 self.mox.ReplayAll()
456 self.assertItemsEqual(
457 [1],
458 self.config_service.LookupIDsOfLabelsMatching(
459 self.cnxn, 789, re.compile('Sec.*')))
460 self.assertItemsEqual(
461 [1, 2],
462 self.config_service.LookupIDsOfLabelsMatching(
463 self.cnxn, 789, re.compile('.*')))
464 self.assertItemsEqual(
465 [],
466 self.config_service.LookupIDsOfLabelsMatching(
467 self.cnxn, 789, re.compile('Zzzzz.*')))
468 self.mox.VerifyAll()
469
470 def SetUpLookupLabelIDsAnyProject(self, label, id_rows):
471 self.config_service.labeldef_tbl.Select(
472 self.cnxn, cols=['id'], label=label).AndReturn(id_rows)
473
474 def testLookupLabelIDsAnyProject(self):
475 self.SetUpLookupLabelIDsAnyProject('Security', [(1,)])
476 self.mox.ReplayAll()
477 actual = self.config_service.LookupLabelIDsAnyProject(
478 self.cnxn, 'Security')
479 self.mox.VerifyAll()
480 self.assertEqual([1], actual)
481
482 def SetUpLookupIDsOfLabelsMatchingAnyProject(self, id_label_rows):
483 self.config_service.labeldef_tbl.Select(
484 self.cnxn, cols=['id', 'label']).AndReturn(id_label_rows)
485
486 def testLookupIDsOfLabelsMatchingAnyProject(self):
487 id_label_rows = [(1, 'Security'), (2, 'UX')]
488 self.SetUpLookupIDsOfLabelsMatchingAnyProject(id_label_rows)
489 self.mox.ReplayAll()
490 actual = self.config_service.LookupIDsOfLabelsMatchingAnyProject(
491 self.cnxn, re.compile('(Sec|Zzz).*'))
492 self.mox.VerifyAll()
493 self.assertEqual([1], actual)
494
495 ### Status lookups
496
497 def testGetStatusDefRows(self):
498 rows = 'foo'
499 self.config_service.status_row_2lc.CacheItem(789, rows)
500 actual = self.config_service.GetStatusDefRows(self.cnxn, 789)
501 self.assertEqual(rows, actual)
502
503 def SetUpGetStatusDefRowsAnyProject(self, rows):
504 self.config_service.statusdef_tbl.Select(
505 self.cnxn, cols=config_svc.STATUSDEF_COLS,
506 order_by=[('rank DESC', []), ('status DESC', [])]).AndReturn(
507 rows)
508
509 def testGetStatusDefRowsAnyProject(self):
510 rows = 'foo'
511 self.SetUpGetStatusDefRowsAnyProject(rows)
512 self.mox.ReplayAll()
513 actual = self.config_service.GetStatusDefRowsAnyProject(self.cnxn)
514 self.mox.VerifyAll()
515 self.assertEqual(rows, actual)
516
517 def testDeserializeStatuses(self):
518 statusdef_rows = [(1, 789, 1, 'New', True, 'doc', False),
519 (2, 789, 2, 'Fixed', False, 'doc', True)]
520 actual = self.config_service._DeserializeStatuses(statusdef_rows)
521 id_to_name, name_to_id, closed_ids = actual
522 self.assertEqual({1: 'New', 2: 'Fixed'}, id_to_name)
523 self.assertEqual({'new': 1, 'fixed': 2}, name_to_id)
524 self.assertEqual([2], closed_ids)
525
526 def testEnsureStatusCacheEntry_Hit(self):
527 status_dicts = 'foo'
528 self.config_service.status_cache.CacheItem(789, status_dicts)
529 # No mock calls set up because none are needed.
530 self.mox.ReplayAll()
531 self.config_service._EnsureStatusCacheEntry(self.cnxn, 789)
532 self.mox.VerifyAll()
533
534 def SetUpEnsureStatusCacheEntry_Miss(self, keys, rows):
535 self.config_service.statusdef_tbl.Select(
536 self.cnxn, cols=config_svc.STATUSDEF_COLS, project_id=keys,
537 order_by=[('rank DESC', []), ('status DESC', [])]).AndReturn(
538 rows)
539
540 def testEnsureStatusCacheEntry_Miss(self):
541 statusdef_rows = [(1, 789, 1, 'New', True, 'doc', False),
542 (2, 789, 2, 'Fixed', False, 'doc', True)]
543 self.SetUpEnsureStatusCacheEntry_Miss([789], statusdef_rows)
544 self.mox.ReplayAll()
545 self.config_service._EnsureStatusCacheEntry(self.cnxn, 789)
546 self.mox.VerifyAll()
547 status_dicts = {1: 'New', 2: 'Fixed'}, {'new': 1, 'fixed': 2}, [2]
548 self.assertEqual(
549 status_dicts, self.config_service.status_cache.GetItem(789))
550
551 def testLookupStatus_Hit(self):
552 status_dicts = {1: 'New', 2: 'Fixed'}, {'new': 1, 'fixed': 2}, [2]
553 self.config_service.status_cache.CacheItem(789, status_dicts)
554 # No mock calls set up because none are needed.
555 self.mox.ReplayAll()
556 self.assertEqual(
557 'New', self.config_service.LookupStatus(self.cnxn, 789, 1))
558 self.assertEqual(
559 'Fixed', self.config_service.LookupStatus(self.cnxn, 789, 2))
560 self.mox.VerifyAll()
561
562 def testLookupStatusID_Hit(self):
563 status_dicts = {1: 'New', 2: 'Fixed'}, {'new': 1, 'fixed': 2}, [2]
564 self.config_service.status_cache.CacheItem(789, status_dicts)
565 # No mock calls set up because none are needed.
566 self.mox.ReplayAll()
567 self.assertEqual(
568 1, self.config_service.LookupStatusID(self.cnxn, 789, 'New'))
569 self.assertEqual(
570 2, self.config_service.LookupStatusID(self.cnxn, 789, 'Fixed'))
571 self.mox.VerifyAll()
572
573 def testLookupStatusIDs_Hit(self):
574 status_dicts = {1: 'New', 2: 'Fixed'}, {'new': 1, 'fixed': 2}, [2]
575 self.config_service.status_cache.CacheItem(789, status_dicts)
576 # No mock calls set up because none are needed.
577 self.mox.ReplayAll()
578 self.assertEqual(
579 [1, 2],
580 self.config_service.LookupStatusIDs(self.cnxn, 789, ['New', 'Fixed']))
581 self.mox.VerifyAll()
582
583 def testLookupClosedStatusIDs_Hit(self):
584 status_dicts = {1: 'New', 2: 'Fixed'}, {'new': 1, 'fixed': 2}, [2]
585 self.config_service.status_cache.CacheItem(789, status_dicts)
586 # No mock calls set up because none are needed.
587 self.mox.ReplayAll()
588 self.assertEqual(
589 [2],
590 self.config_service.LookupClosedStatusIDs(self.cnxn, 789))
591 self.mox.VerifyAll()
592
593 def SetUpLookupClosedStatusIDsAnyProject(self, id_rows):
594 self.config_service.statusdef_tbl.Select(
595 self.cnxn, cols=['id'], means_open=False).AndReturn(
596 id_rows)
597
598 def testLookupClosedStatusIDsAnyProject(self):
599 self.SetUpLookupClosedStatusIDsAnyProject([(2,)])
600 self.mox.ReplayAll()
601 actual = self.config_service.LookupClosedStatusIDsAnyProject(self.cnxn)
602 self.mox.VerifyAll()
603 self.assertEqual([2], actual)
604
605 def SetUpLookupStatusIDsAnyProject(self, status, id_rows):
606 self.config_service.statusdef_tbl.Select(
607 self.cnxn, cols=['id'], status=status).AndReturn(id_rows)
608
609 def testLookupStatusIDsAnyProject(self):
610 self.SetUpLookupStatusIDsAnyProject('New', [(1,)])
611 self.mox.ReplayAll()
612 actual = self.config_service.LookupStatusIDsAnyProject(self.cnxn, 'New')
613 self.mox.VerifyAll()
614 self.assertEqual([1], actual)
615
616 ### Issue tracker configuration objects
617
618 def SetUpGetProjectConfigs(self, project_ids):
619 self.config_service.projectissueconfig_tbl.Select(
620 self.cnxn, cols=config_svc.PROJECTISSUECONFIG_COLS,
621 project_id=project_ids).AndReturn([])
622 self.config_service.statusdef_tbl.Select(
623 self.cnxn, cols=config_svc.STATUSDEF_COLS,
624 project_id=project_ids, where=[('rank IS NOT NULL', [])],
625 order_by=[('rank', [])]).AndReturn([])
626 self.config_service.labeldef_tbl.Select(
627 self.cnxn, cols=config_svc.LABELDEF_COLS,
628 project_id=project_ids, where=[('rank IS NOT NULL', [])],
629 order_by=[('rank', [])]).AndReturn([])
630 self.config_service.approvaldef2approver_tbl.Select(
631 self.cnxn, cols=config_svc.APPROVALDEF2APPROVER_COLS,
632 project_id=project_ids).AndReturn([])
633 self.config_service.approvaldef2survey_tbl.Select(
634 self.cnxn, cols=config_svc.APPROVALDEF2SURVEY_COLS,
635 project_id=project_ids).AndReturn([])
636 self.config_service.fielddef_tbl.Select(
637 self.cnxn, cols=config_svc.FIELDDEF_COLS,
638 project_id=project_ids, order_by=[('field_name', [])]).AndReturn([])
639 self.config_service.componentdef_tbl.Select(
640 self.cnxn, cols=config_svc.COMPONENTDEF_COLS,
641 is_deleted=False,
642 project_id=project_ids, order_by=[('path', [])]).AndReturn([])
643
644 def testGetProjectConfigs(self):
645 project_ids = [789, 679]
646 self.SetUpGetProjectConfigs(project_ids)
647
648 self.mox.ReplayAll()
649 config_dict = self.config_service.GetProjectConfigs(
650 self.cnxn, [789, 679], use_cache=False)
651 self.assertEqual(2, len(config_dict))
652 for pid in project_ids:
653 self.assertEqual(pid, config_dict[pid].project_id)
654 self.mox.VerifyAll()
655
656 def testGetProjectConfig_Hit(self):
657 project_id = 789
658 config = tracker_bizobj.MakeDefaultProjectIssueConfig(project_id)
659 self.config_service.config_2lc.CacheItem(project_id, config)
660
661 self.mox.ReplayAll()
662 actual = self.config_service.GetProjectConfig(self.cnxn, project_id)
663 self.assertEqual(config, actual)
664 self.mox.VerifyAll()
665
666 def testGetProjectConfig_Miss(self):
667 project_id = 789
668 self.SetUpGetProjectConfigs([project_id])
669
670 self.mox.ReplayAll()
671 config = self.config_service.GetProjectConfig(self.cnxn, project_id)
672 self.assertEqual(project_id, config.project_id)
673 self.mox.VerifyAll()
674
675 def SetUpStoreConfig_Default(self, project_id):
676 self.config_service.projectissueconfig_tbl.InsertRow(
677 self.cnxn, replace=True,
678 project_id=project_id,
679 statuses_offer_merge='Duplicate',
680 exclusive_label_prefixes='Type Priority Milestone',
681 default_template_for_developers=0,
682 default_template_for_users=0,
683 default_col_spec=tracker_constants.DEFAULT_COL_SPEC,
684 default_sort_spec='',
685 default_x_attr='',
686 default_y_attr='',
687 member_default_query='',
688 custom_issue_entry_url=None,
689 commit=False)
690
691 self.SetUpUpdateWellKnownLabels_Default(project_id)
692 self.SetUpUpdateWellKnownStatuses_Default(project_id)
693 self.cnxn.Commit()
694
695 def SetUpUpdateWellKnownLabels_JustCache(self, project_id):
696 by_id = {
697 idx + 1: label for idx, (label, _, _) in enumerate(
698 tracker_constants.DEFAULT_WELL_KNOWN_LABELS)}
699 by_name = {name.lower(): label_id
700 for label_id, name in by_id.items()}
701 label_dicts = by_id, by_name
702 self.config_service.label_cache.CacheAll({project_id: label_dicts})
703
704 def SetUpUpdateWellKnownLabels_Default(self, project_id):
705 self.SetUpUpdateWellKnownLabels_JustCache(project_id)
706 update_labeldef_rows = [
707 (idx + 1, project_id, idx, label, doc, deprecated)
708 for idx, (label, doc, deprecated) in enumerate(
709 tracker_constants.DEFAULT_WELL_KNOWN_LABELS)]
710 self.config_service.labeldef_tbl.Update(
711 self.cnxn, {'rank': None}, project_id=project_id, commit=False)
712 self.config_service.labeldef_tbl.InsertRows(
713 self.cnxn, config_svc.LABELDEF_COLS, update_labeldef_rows,
714 replace=True, commit=False)
715 self.config_service.labeldef_tbl.InsertRows(
716 self.cnxn, config_svc.LABELDEF_COLS[1:], [], commit=False)
717
718 def SetUpUpdateWellKnownStatuses_Default(self, project_id):
719 by_id = {
720 idx + 1: status for idx, (status, _, _, _) in enumerate(
721 tracker_constants.DEFAULT_WELL_KNOWN_STATUSES)}
722 by_name = {name.lower(): label_id
723 for label_id, name in by_id.items()}
724 closed_ids = [
725 idx + 1 for idx, (_, _, means_open, _) in enumerate(
726 tracker_constants.DEFAULT_WELL_KNOWN_STATUSES)
727 if not means_open]
728 status_dicts = by_id, by_name, closed_ids
729 self.config_service.status_cache.CacheAll({789: status_dicts})
730
731 update_statusdef_rows = [
732 (idx + 1, project_id, idx, status, means_open, doc, deprecated)
733 for idx, (status, doc, means_open, deprecated) in enumerate(
734 tracker_constants.DEFAULT_WELL_KNOWN_STATUSES)]
735 self.config_service.statusdef_tbl.Update(
736 self.cnxn, {'rank': None}, project_id=project_id, commit=False)
737 self.config_service.statusdef_tbl.InsertRows(
738 self.cnxn, config_svc.STATUSDEF_COLS, update_statusdef_rows,
739 replace=True, commit=False)
740 self.config_service.statusdef_tbl.InsertRows(
741 self.cnxn, config_svc.STATUSDEF_COLS[1:], [], commit=False)
742
743 def SetUpUpdateApprovals_Default(
744 self, approval_id, approver_rows, survey_row):
745 self.config_service.approvaldef2approver_tbl.Delete(
746 self.cnxn, approval_id=approval_id, commit=False)
747
748 self.config_service.approvaldef2approver_tbl.InsertRows(
749 self.cnxn,
750 config_svc.APPROVALDEF2APPROVER_COLS,
751 approver_rows,
752 commit=False)
753
754 approval_id, survey, project_id = survey_row
755 self.config_service.approvaldef2survey_tbl.Delete(
756 self.cnxn, approval_id=approval_id, commit=False)
757 self.config_service.approvaldef2survey_tbl.InsertRow(
758 self.cnxn,
759 approval_id=approval_id,
760 survey=survey,
761 project_id=project_id,
762 commit=False)
763
764 def testStoreConfig(self):
765 config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
766 self.SetUpStoreConfig_Default(789)
767
768 self.mox.ReplayAll()
769 self.config_service.StoreConfig(self.cnxn, config)
770 self.mox.VerifyAll()
771
772 def testUpdateWellKnownLabels(self):
773 config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
774 self.SetUpUpdateWellKnownLabels_Default(789)
775
776 self.mox.ReplayAll()
777 self.config_service._UpdateWellKnownLabels(self.cnxn, config)
778 self.mox.VerifyAll()
779
780 def testUpdateWellKnownLabels_Duplicate(self):
781 config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
782 config.well_known_labels.append(config.well_known_labels[0])
783 self.SetUpUpdateWellKnownLabels_JustCache(789)
784
785 self.mox.ReplayAll()
786 with self.assertRaises(exceptions.InputException) as cm:
787 self.config_service._UpdateWellKnownLabels(self.cnxn, config)
788 self.mox.VerifyAll()
789 self.assertEqual(
790 'Defined label "Type-Defect" twice',
791 cm.exception.message)
792
793 def testUpdateWellKnownStatuses(self):
794 config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
795 self.SetUpUpdateWellKnownStatuses_Default(789)
796
797 self.mox.ReplayAll()
798 self.config_service._UpdateWellKnownStatuses(self.cnxn, config)
799 self.mox.VerifyAll()
800
801 def testUpdateApprovals(self):
802 config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
803 approver_rows = [(123, 111, 789), (123, 222, 789)]
804 survey_row = (123, 'Q1\nQ2', 789)
805 first_approval = tracker_bizobj.MakeFieldDef(
806 123, 789, 'FirstApproval', tracker_pb2.FieldTypes.APPROVAL_TYPE,
807 None, '', False, False, False, None, None, '', False, '', '',
808 tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'the first one', False)
809 config.field_defs = [first_approval]
810 config.approval_defs = [tracker_pb2.ApprovalDef(
811 approval_id=123, approver_ids=[111, 222], survey='Q1\nQ2')]
812 self.SetUpUpdateApprovals_Default(123, approver_rows, survey_row)
813
814 self.mox.ReplayAll()
815 self.config_service._UpdateApprovals(self.cnxn, config)
816 self.mox.VerifyAll()
817
818 def testUpdateConfig(self):
819 pass # TODO(jrobbins): add a test for this
820
821 def SetUpExpungeConfig(self, project_id):
822 self.config_service.statusdef_tbl.Delete(self.cnxn, project_id=project_id)
823 self.config_service.labeldef_tbl.Delete(self.cnxn, project_id=project_id)
824 self.config_service.projectissueconfig_tbl.Delete(
825 self.cnxn, project_id=project_id)
826
827 self.config_service.config_2lc.InvalidateKeys(self.cnxn, [project_id])
828
829 def testExpungeConfig(self):
830 self.SetUpExpungeConfig(789)
831
832 self.mox.ReplayAll()
833 self.config_service.ExpungeConfig(self.cnxn, 789)
834 self.mox.VerifyAll()
835
836 def testExpungeUsersInConfigs(self):
837
838 self.config_service.component2admin_tbl.Delete = mock.Mock()
839 self.config_service.component2cc_tbl.Delete = mock.Mock()
840 self.config_service.componentdef_tbl.Update = mock.Mock()
841
842 self.config_service.fielddef2admin_tbl.Delete = mock.Mock()
843 self.config_service.fielddef2editor_tbl.Delete = mock.Mock()
844 self.config_service.approvaldef2approver_tbl.Delete = mock.Mock()
845
846 user_ids = [111, 222, 333]
847 self.config_service.ExpungeUsersInConfigs(self.cnxn, user_ids, limit=50)
848
849 self.config_service.component2admin_tbl.Delete.assert_called_once_with(
850 self.cnxn, admin_id=user_ids, commit=False, limit=50)
851 self.config_service.component2cc_tbl.Delete.assert_called_once_with(
852 self.cnxn, cc_id=user_ids, commit=False, limit=50)
853 cdef_calls = [
854 mock.call(
855 self.cnxn, {'creator_id': framework_constants.DELETED_USER_ID},
856 creator_id=user_ids, commit=False, limit=50),
857 mock.call(
858 self.cnxn, {'modifier_id': framework_constants.DELETED_USER_ID},
859 modifier_id=user_ids, commit=False, limit=50)]
860 self.config_service.componentdef_tbl.Update.assert_has_calls(cdef_calls)
861
862 self.config_service.fielddef2admin_tbl.Delete.assert_called_once_with(
863 self.cnxn, admin_id=user_ids, commit=False, limit=50)
864 self.config_service.fielddef2editor_tbl.Delete.assert_called_once_with(
865 self.cnxn, editor_id=user_ids, commit=False, limit=50)
866 self.config_service.approvaldef2approver_tbl.Delete.assert_called_once_with(
867 self.cnxn, approver_id=user_ids, commit=False, limit=50)
868
869 ### Custom field definitions
870
871 def SetUpCreateFieldDef(self, project_id):
872 self.config_service.fielddef_tbl.InsertRow(
873 self.cnxn,
874 project_id=project_id,
875 field_name='PercentDone',
876 field_type='int_type',
877 applicable_type='Defect',
878 applicable_predicate='',
879 is_required=False,
880 is_multivalued=False,
881 is_niche=False,
882 min_value=1,
883 max_value=100,
884 regex=None,
885 needs_member=None,
886 needs_perm=None,
887 grants_perm=None,
888 notify_on='never',
889 date_action='no_action',
890 docstring='doc',
891 approval_id=None,
892 is_phase_field=False,
893 is_restricted_field=True,
894 commit=False).AndReturn(1)
895 self.config_service.fielddef2admin_tbl.InsertRows(
896 self.cnxn, config_svc.FIELDDEF2ADMIN_COLS, [(1, 111)], commit=False)
897 self.config_service.fielddef2editor_tbl.InsertRows(
898 self.cnxn, config_svc.FIELDDEF2EDITOR_COLS, [(1, 222)], commit=False)
899 self.cnxn.Commit()
900
901 def testCreateFieldDef(self):
902 self.SetUpCreateFieldDef(789)
903
904 self.mox.ReplayAll()
905 field_id = self.config_service.CreateFieldDef(
906 self.cnxn,
907 789,
908 'PercentDone',
909 'int_type',
910 'Defect',
911 '',
912 False,
913 False,
914 False,
915 1,
916 100,
917 None,
918 None,
919 None,
920 None,
921 0,
922 'no_action',
923 'doc', [111], [222],
924 is_restricted_field=True)
925 self.mox.VerifyAll()
926 self.assertEqual(1, field_id)
927
928 def SetUpSoftDeleteFieldDefs(self, field_ids):
929 self.config_service.fielddef_tbl.Update(
930 self.cnxn, {'is_deleted': True}, id=field_ids)
931
932 def testSoftDeleteFieldDefs(self):
933 self.SetUpSoftDeleteFieldDefs([1])
934
935 self.mox.ReplayAll()
936 self.config_service.SoftDeleteFieldDefs(self.cnxn, 789, [1])
937 self.mox.VerifyAll()
938
939 def SetUpUpdateFieldDef(self, field_id, new_values, admin_rows, editor_rows):
940 self.config_service.fielddef_tbl.Update(
941 self.cnxn, new_values, id=field_id, commit=False)
942 self.config_service.fielddef2admin_tbl.Delete(
943 self.cnxn, field_id=field_id, commit=False)
944 self.config_service.fielddef2admin_tbl.InsertRows(
945 self.cnxn, config_svc.FIELDDEF2ADMIN_COLS, admin_rows, commit=False)
946 self.config_service.fielddef2editor_tbl.Delete(
947 self.cnxn, field_id=field_id, commit=False)
948 self.config_service.fielddef2editor_tbl.InsertRows(
949 self.cnxn, config_svc.FIELDDEF2EDITOR_COLS, editor_rows, commit=False)
950 self.cnxn.Commit()
951
952 def testUpdateFieldDef_NoOp(self):
953 new_values = {}
954 self.SetUpUpdateFieldDef(1, new_values, [], [])
955
956 self.mox.ReplayAll()
957 self.config_service.UpdateFieldDef(
958 self.cnxn, 789, 1, admin_ids=[], editor_ids=[])
959 self.mox.VerifyAll()
960
961 def testUpdateFieldDef_Normal(self):
962 new_values = dict(
963 field_name='newname',
964 applicable_type='defect',
965 applicable_predicate='pri:1',
966 is_required=True,
967 is_niche=True,
968 is_multivalued=True,
969 min_value=32,
970 max_value=212,
971 regex='a.*b',
972 needs_member=True,
973 needs_perm='EditIssue',
974 grants_perm='DeleteIssue',
975 notify_on='any_comment',
976 docstring='new doc',
977 is_restricted_field=True)
978 self.SetUpUpdateFieldDef(1, new_values, [(1, 111)], [(1, 222)])
979
980 self.mox.ReplayAll()
981 new_values = new_values.copy()
982 new_values['notify_on'] = 1
983 self.config_service.UpdateFieldDef(
984 self.cnxn, 789, 1, admin_ids=[111], editor_ids=[222], **new_values)
985 self.mox.VerifyAll()
986
987 ### Component definitions
988
989 def SetUpFindMatchingComponentIDsAnyProject(self, _exact, rows):
990 # TODO(jrobbins): more details here.
991 self.config_service.componentdef_tbl.Select(
992 self.cnxn, cols=['id'], where=mox.IsA(list)).AndReturn(rows)
993
994 def testFindMatchingComponentIDsAnyProject_Rooted(self):
995 self.SetUpFindMatchingComponentIDsAnyProject(True, [(1,), (2,), (3,)])
996
997 self.mox.ReplayAll()
998 comp_ids = self.config_service.FindMatchingComponentIDsAnyProject(
999 self.cnxn, ['WindowManager', 'NetworkLayer'])
1000 self.mox.VerifyAll()
1001 self.assertItemsEqual([1, 2, 3], comp_ids)
1002
1003 def testFindMatchingComponentIDsAnyProject_NonRooted(self):
1004 self.SetUpFindMatchingComponentIDsAnyProject(False, [(1,), (2,), (3,)])
1005
1006 self.mox.ReplayAll()
1007 comp_ids = self.config_service.FindMatchingComponentIDsAnyProject(
1008 self.cnxn, ['WindowManager', 'NetworkLayer'], exact=False)
1009 self.mox.VerifyAll()
1010 self.assertItemsEqual([1, 2, 3], comp_ids)
1011
1012 def SetUpCreateComponentDef(self, comp_id):
1013 self.config_service.componentdef_tbl.InsertRow(
1014 self.cnxn, project_id=789, path='WindowManager',
1015 docstring='doc', deprecated=False, commit=False,
1016 created=0, creator_id=0).AndReturn(comp_id)
1017 self.config_service.component2admin_tbl.InsertRows(
1018 self.cnxn, config_svc.COMPONENT2ADMIN_COLS, [], commit=False)
1019 self.config_service.component2cc_tbl.InsertRows(
1020 self.cnxn, config_svc.COMPONENT2CC_COLS, [], commit=False)
1021 self.config_service.component2label_tbl.InsertRows(
1022 self.cnxn, config_svc.COMPONENT2LABEL_COLS, [], commit=False)
1023 self.cnxn.Commit()
1024
1025 def testCreateComponentDef(self):
1026 self.SetUpCreateComponentDef(1)
1027
1028 self.mox.ReplayAll()
1029 comp_id = self.config_service.CreateComponentDef(
1030 self.cnxn, 789, 'WindowManager', 'doc', False, [], [], 0, 0, [])
1031 self.mox.VerifyAll()
1032 self.assertEqual(1, comp_id)
1033
1034 def SetUpUpdateComponentDef(self, component_id):
1035 self.config_service.component2admin_tbl.Delete(
1036 self.cnxn, component_id=component_id, commit=False)
1037 self.config_service.component2admin_tbl.InsertRows(
1038 self.cnxn, config_svc.COMPONENT2ADMIN_COLS, [], commit=False)
1039 self.config_service.component2cc_tbl.Delete(
1040 self.cnxn, component_id=component_id, commit=False)
1041 self.config_service.component2cc_tbl.InsertRows(
1042 self.cnxn, config_svc.COMPONENT2CC_COLS, [], commit=False)
1043 self.config_service.component2label_tbl.Delete(
1044 self.cnxn, component_id=component_id, commit=False)
1045 self.config_service.component2label_tbl.InsertRows(
1046 self.cnxn, config_svc.COMPONENT2LABEL_COLS, [], commit=False)
1047
1048 self.config_service.componentdef_tbl.Update(
1049 self.cnxn,
1050 {'path': 'DisplayManager', 'docstring': 'doc', 'deprecated': True},
1051 id=component_id, commit=False)
1052 self.cnxn.Commit()
1053
1054 def testUpdateComponentDef(self):
1055 self.SetUpUpdateComponentDef(1)
1056
1057 self.mox.ReplayAll()
1058 self.config_service.UpdateComponentDef(
1059 self.cnxn, 789, 1, path='DisplayManager', docstring='doc',
1060 deprecated=True, admin_ids=[], cc_ids=[], label_ids=[])
1061 self.mox.VerifyAll()
1062
1063 def SetUpSoftDeleteComponentDef(self, component_id):
1064 self.config_service.componentdef_tbl.Update(
1065 self.cnxn, {'is_deleted': True}, commit=False, id=component_id)
1066 self.cnxn.Commit()
1067
1068 def testSoftDeleteComponentDef(self):
1069 self.SetUpSoftDeleteComponentDef(1)
1070
1071 self.mox.ReplayAll()
1072 self.config_service.DeleteComponentDef(self.cnxn, 789, 1)
1073 self.mox.VerifyAll()
1074
1075 ### Memcache management
1076
1077 def testInvalidateMemcache(self):
1078 pass # TODO(jrobbins): write this
1079
1080 def testInvalidateMemcacheShards(self):
1081 NOW = 1234567
1082 memcache.set('789;1', NOW)
1083 memcache.set('789;2', NOW - 1000)
1084 memcache.set('789;3', NOW - 2000)
1085 memcache.set('all;1', NOW)
1086 memcache.set('all;2', NOW - 1000)
1087 memcache.set('all;3', NOW - 2000)
1088
1089 # Delete some of them.
1090 self.config_service._InvalidateMemcacheShards(
1091 [(789, 1), (789, 2), (789,9)])
1092
1093 self.assertIsNone(memcache.get('789;1'))
1094 self.assertIsNone(memcache.get('789;2'))
1095 self.assertEqual(NOW - 2000, memcache.get('789;3'))
1096 self.assertIsNone(memcache.get('all;1'))
1097 self.assertIsNone(memcache.get('all;2'))
1098 self.assertEqual(NOW - 2000, memcache.get('all;3'))
1099
1100 def testInvalidateMemcacheForEntireProject(self):
1101 NOW = 1234567
1102 memcache.set('789;1', NOW)
1103 memcache.set('config:789', 'serialized config')
1104 memcache.set('label_rows:789', 'serialized label rows')
1105 memcache.set('status_rows:789', 'serialized status rows')
1106 memcache.set('field_rows:789', 'serialized field rows')
1107 memcache.set('890;1', NOW) # Other projects will not be affected.
1108
1109 self.config_service.InvalidateMemcacheForEntireProject(789)
1110
1111 self.assertIsNone(memcache.get('789;1'))
1112 self.assertIsNone(memcache.get('config:789'))
1113 self.assertIsNone(memcache.get('status_rows:789'))
1114 self.assertIsNone(memcache.get('label_rows:789'))
1115 self.assertIsNone(memcache.get('field_rows:789'))
1116 self.assertEqual(NOW, memcache.get('890;1'))
1117
1118 def testUsersInvolvedInConfig_Empty(self):
1119 templates = []
1120 config = tracker_pb2.ProjectIssueConfig()
1121 self.assertEqual(set(), self.config_service.UsersInvolvedInConfig(
1122 config, templates))
1123
1124 def testUsersInvolvedInConfig_Default(self):
1125 templates = [
1126 tracker_bizobj.ConvertDictToTemplate(t)
1127 for t in tracker_constants.DEFAULT_TEMPLATES]
1128 config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
1129 self.assertEqual(set(), self.config_service.UsersInvolvedInConfig(
1130 config, templates))
1131
1132 def testUsersInvolvedInConfig_Normal(self):
1133 config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
1134 templates = [
1135 tracker_bizobj.ConvertDictToTemplate(t)
1136 for t in tracker_constants.DEFAULT_TEMPLATES]
1137 templates[0].owner_id = 111
1138 templates[0].admin_ids = [111, 222]
1139 config.field_defs = [
1140 tracker_pb2.FieldDef(admin_ids=[333], editor_ids=[444])
1141 ]
1142 actual = self.config_service.UsersInvolvedInConfig(config, templates)
1143 self.assertEqual({111, 222, 333, 444}, actual)