blob: 212f5a625858c8f6bafb1e35c113c291def9177d [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"""Tests for the backendsearchpipeline module."""
7from __future__ import print_function
8from __future__ import division
9from __future__ import absolute_import
10
11import mox
12import unittest
13
14from google.appengine.api import memcache
15from google.appengine.ext import testbed
16
17import settings
18from framework import framework_helpers
19from framework import sorting
20from framework import sql
21from proto import ast_pb2
22from proto import tracker_pb2
23from search import backendsearchpipeline
24from search import ast2ast
25from search import query2ast
26from services import service_manager
27from services import tracker_fulltext
28from testing import fake
29from testing import testing_helpers
30from tracker import tracker_bizobj
31
32
33class BackendSearchPipelineTest(unittest.TestCase):
34
35 def setUp(self):
36 self.cnxn = 'fake cnxn'
37 self.services = service_manager.Services(
38 user=fake.UserService(),
39 usergroup=fake.UserGroupService(),
40 project=fake.ProjectService(),
41 issue=fake.IssueService(),
42 config=fake.ConfigService(),
43 cache_manager=fake.CacheManager())
44 self.services.user.TestAddUser('a@example.com', 111)
45 self.project = self.services.project.TestAddProject('proj', project_id=789)
46 self.mr = testing_helpers.MakeMonorailRequest(
47 path='/p/proj/issues/list?q=Priority:High',
48 project=self.project)
49 self.mr.me_user_id = 999 # This value is not used by backend search
50 self.mr.shard_id = 2
51 self.mr.invalidation_timestep = 12345
52
53 self.mox = mox.Mox()
54 self.testbed = testbed.Testbed()
55 self.testbed.activate()
56 self.testbed.init_user_stub()
57 self.testbed.init_memcache_stub()
58 sorting.InitializeArtValues(self.services)
59
60 def tearDown(self):
61 self.testbed.deactivate()
62 self.mox.UnsetStubs()
63 self.mox.ResetAll()
64
65 def SetUpPromises(self, exp_query):
66 self.mox.StubOutWithMock(framework_helpers, 'Promise')
67 framework_helpers.Promise(
68 backendsearchpipeline._GetQueryResultIIDs, self.mr.cnxn,
69 self.services, 'is:open', exp_query, [789],
70 mox.IsA(tracker_pb2.ProjectIssueConfig), ['project', 'id'],
71 ('Issue.shard = %s', [2]), 2, self.mr.invalidation_timestep
72 ).AndReturn('fake promise 1')
73
74 def testMakePromises_Anon(self):
75 """A backend pipeline does not personalize the query of anon users."""
76 self.SetUpPromises('Priority:High')
77 self.mox.ReplayAll()
78 backendsearchpipeline.BackendSearchPipeline(
79 self.mr, self.services, 100, ['proj'], None, [])
80 self.mox.VerifyAll()
81
82 def testMakePromises_SignedIn(self):
83 """A backend pipeline immediately personalizes and runs the query."""
84 self.mr.query = 'owner:me'
85 self.SetUpPromises('owner:111')
86 self.mox.ReplayAll()
87 backendsearchpipeline.BackendSearchPipeline(
88 self.mr, self.services, 100, ['proj'], 111, [111])
89 self.mox.VerifyAll()
90
91 def testSearchForIIDs(self):
92 self.SetUpPromises('Priority:High')
93 self.mox.ReplayAll()
94 be_pipeline = backendsearchpipeline.BackendSearchPipeline(
95 self.mr, self.services, 100, ['proj'], 111, [111])
96 be_pipeline.result_iids_promise = testing_helpers.Blank(
97 WaitAndGetValue=lambda: ([10002, 10052], False, None))
98 be_pipeline.SearchForIIDs()
99 self.mox.VerifyAll()
100 self.assertEqual([10002, 10052], be_pipeline.result_iids)
101 self.assertEqual(False, be_pipeline.search_limit_reached)
102
103
104class BackendSearchPipelineMethodsTest(unittest.TestCase):
105
106 def setUp(self):
107 self.cnxn = 'fake cnxn'
108 self.config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
109 self.services = service_manager.Services(
110 user=fake.UserService(),
111 usergroup=fake.UserGroupService(),
112 project=fake.ProjectService(),
113 issue=fake.IssueService(),
114 config=fake.ConfigService(),
115 cache_manager=fake.CacheManager())
116 self.services.user.TestAddUser('a@example.com', 111)
117 self.project = self.services.project.TestAddProject('proj', project_id=789)
118 self.mr = testing_helpers.MakeMonorailRequest(
119 path='/p/proj/issues/list?q=Priority:High',
120 project=self.project)
121
122 self.mox = mox.Mox()
123 self.testbed = testbed.Testbed()
124 self.testbed.activate()
125 self.testbed.init_user_stub()
126 self.testbed.init_memcache_stub()
127
128 def tearDown(self):
129 self.testbed.deactivate()
130 self.mox.UnsetStubs()
131 self.mox.ResetAll()
132
133 def testSearchProjectCan_Normal(self):
134 query_ast = query2ast.ParseUserQuery(
135 'Priority:High', 'is:open', query2ast.BUILTIN_ISSUE_FIELDS,
136 self.config)
137 simplified_query_ast = ast2ast.PreprocessAST(
138 self.cnxn, query_ast, [789], self.services, self.config)
139 conj = simplified_query_ast.conjunctions[0]
140 self.mox.StubOutWithMock(tracker_fulltext, 'SearchIssueFullText')
141 tracker_fulltext.SearchIssueFullText(
142 [789], conj, 2).AndReturn((None, False))
143 self.mox.StubOutWithMock(self.services.issue, 'RunIssueQuery')
144 self.services.issue.RunIssueQuery(
145 self.cnxn, mox.IsA(list), mox.IsA(list), mox.IsA(list),
146 shard_id=2).AndReturn(([10002, 10052], False))
147 self.mox.ReplayAll()
148 result, capped, err = backendsearchpipeline.SearchProjectCan(
149 self.cnxn, self.services, [789], query_ast, 2, self.config)
150 self.mox.VerifyAll()
151 self.assertEqual([10002, 10052], result)
152 self.assertFalse(capped)
153 self.assertEqual(None, err)
154
155 def testSearchProjectCan_DBCapped(self):
156 query_ast = query2ast.ParseUserQuery(
157 'Priority:High', 'is:open', query2ast.BUILTIN_ISSUE_FIELDS,
158 self.config)
159 simplified_query_ast = ast2ast.PreprocessAST(
160 self.cnxn, query_ast, [789], self.services, self.config)
161 conj = simplified_query_ast.conjunctions[0]
162 self.mox.StubOutWithMock(tracker_fulltext, 'SearchIssueFullText')
163 tracker_fulltext.SearchIssueFullText(
164 [789], conj, 2).AndReturn((None, False))
165 self.mox.StubOutWithMock(self.services.issue, 'RunIssueQuery')
166 self.services.issue.RunIssueQuery(
167 self.cnxn, mox.IsA(list), mox.IsA(list), mox.IsA(list),
168 shard_id=2).AndReturn(([10002, 10052], True))
169 self.mox.ReplayAll()
170 result, capped, err = backendsearchpipeline.SearchProjectCan(
171 self.cnxn, self.services, [789], query_ast, 2, self.config)
172 self.mox.VerifyAll()
173 self.assertEqual([10002, 10052], result)
174 self.assertTrue(capped)
175 self.assertEqual(None, err)
176
177 def testSearchProjectCan_FTSCapped(self):
178 query_ast = query2ast.ParseUserQuery(
179 'Priority:High', 'is:open', query2ast.BUILTIN_ISSUE_FIELDS,
180 self.config)
181 simplified_query_ast = ast2ast.PreprocessAST(
182 self.cnxn, query_ast, [789], self.services, self.config)
183 conj = simplified_query_ast.conjunctions[0]
184 self.mox.StubOutWithMock(tracker_fulltext, 'SearchIssueFullText')
185 tracker_fulltext.SearchIssueFullText(
186 [789], conj, 2).AndReturn(([10002, 10052], True))
187 self.mox.StubOutWithMock(self.services.issue, 'RunIssueQuery')
188 self.services.issue.RunIssueQuery(
189 self.cnxn, mox.IsA(list), mox.IsA(list), mox.IsA(list),
190 shard_id=2).AndReturn(([10002, 10052], False))
191 self.mox.ReplayAll()
192 result, capped, err = backendsearchpipeline.SearchProjectCan(
193 self.cnxn, self.services, [789], query_ast, 2, self.config)
194 self.mox.VerifyAll()
195 self.assertEqual([10002, 10052], result)
196 self.assertTrue(capped)
197 self.assertEqual(None, err)
198
199 def testGetQueryResultIIDs(self):
200 sd = ['project', 'id']
201 slice_term = ('Issue.shard = %s', [2])
202 query_ast = query2ast.ParseUserQuery(
203 'Priority:High', 'is:open', query2ast.BUILTIN_ISSUE_FIELDS,
204 self.config)
205 query_ast = backendsearchpipeline._FilterSpam(query_ast)
206
207 self.mox.StubOutWithMock(backendsearchpipeline, 'SearchProjectCan')
208 backendsearchpipeline.SearchProjectCan(
209 self.cnxn, self.services, [789], query_ast, 2, self.config,
210 sort_directives=sd, where=[slice_term],
211 query_desc='getting query issue IDs'
212 ).AndReturn(([10002, 10052], False, None))
213 self.mox.ReplayAll()
214 result, capped, err = backendsearchpipeline._GetQueryResultIIDs(
215 self.cnxn, self.services, 'is:open', 'Priority:High',
216 [789], self.config, sd, slice_term, 2, 12345)
217 self.mox.VerifyAll()
218 self.assertEqual([10002, 10052], result)
219 self.assertFalse(capped)
220 self.assertEqual(None, err)
221 self.assertEqual(
222 ([10002, 10052], 12345),
223 memcache.get('789;is:open;Priority:High;project id;2'))
224
225 def testGetSpamQueryResultIIDs(self):
226 sd = ['project', 'id']
227 slice_term = ('Issue.shard = %s', [2])
228 query_ast = query2ast.ParseUserQuery(
229 'Priority:High is:spam', 'is:open', query2ast.BUILTIN_ISSUE_FIELDS,
230 self.config)
231
232 query_ast = backendsearchpipeline._FilterSpam(query_ast)
233
234 self.mox.StubOutWithMock(backendsearchpipeline, 'SearchProjectCan')
235 backendsearchpipeline.SearchProjectCan(
236 self.cnxn, self.services, [789], query_ast, 2, self.config,
237 sort_directives=sd, where=[slice_term],
238 query_desc='getting query issue IDs'
239 ).AndReturn(([10002, 10052], False, None))
240 self.mox.ReplayAll()
241 result, capped, err = backendsearchpipeline._GetQueryResultIIDs(
242 self.cnxn, self.services, 'is:open', 'Priority:High is:spam',
243 [789], self.config, sd, slice_term, 2, 12345)
244 self.mox.VerifyAll()
245 self.assertEqual([10002, 10052], result)
246 self.assertFalse(capped)
247 self.assertEqual(None, err)
248 self.assertEqual(
249 ([10002, 10052], 12345),
250 memcache.get('789;is:open;Priority:High is:spam;project id;2'))