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