/
testQueryCatalog.py
266 lines (218 loc) · 10.4 KB
/
testQueryCatalog.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# Test queryCatalog and plone search forms
from plone.app.textfield.value import RichTextValue
from plone.base.interfaces import INavigationSchema
from plone.base.interfaces import ISearchSchema
from plone.base.interfaces.syndication import ISiteSyndicationSettings
from plone.registry.interfaces import IRegistry
from Products.CMFPlone.tests import PloneTestCase
from Products.ZCTextIndex.ParseTree import ParseError
from zExceptions import NotFound
from zope.component import getMultiAdapter
from zope.component import getUtility
class TestQueryCatalog(PloneTestCase.PloneTestCase):
"""Test queryCatalog script.
Test function of queryCatalog script, **not** the
functionality of the catalog itself. Therefore, we'll replace
the actual call to the catalog to a dummy routine that just
returns the catalog search dictionary so we can examine what
would be searched.
"""
def dummyCatalog(self, REQUEST=None, **kw):
return kw
def stripStuff(self, query_dict):
# strip portal_types and show_inactive parameter which is
# auto-set with types blacklisting. Useful to simplify test
# assertions when we don't care
if isinstance(query_dict, dict):
for ignore in ["portal_type", "show_inactive"]:
if ignore in query_dict:
del query_dict[ignore]
return query_dict
def afterSetUp(self):
self.portal.portal_catalog.__call__ = self.dummyCatalog
def testEmptyRequest(self):
request = {}
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), [])
def testNonexistantIndex(self):
request = {"foo": "bar"}
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), [])
def testRealIndex(self):
request = {"SearchableText": "bar"}
self.assertEqual(
self.stripStuff(self.folder.queryCatalog(request)),
{"SearchableText": "bar"},
)
def testTwoIndexes(self):
request = {"SearchableText": "bar", "foo": "bar"}
self.assertEqual(
self.stripStuff(self.folder.queryCatalog(request)),
{"SearchableText": "bar"},
)
def testRealIndexes(self):
request = {"SearchableText": "bar", "Subject": "bar"}
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), request)
def testOnlySort(self):
# if we only sort, we shouldn't actually call the catalog
request = {"sort_on": "foozle"}
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), [])
request = {"sort_order": "foozle", "sort_on": "foozle"}
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), [])
request = {"sort_order": "foozle"}
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), [])
def testOnlyUsage(self):
request = {"date_usage": "range:min"}
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), [])
def testRealWithUsage(self):
request = {"modified": "2004-01-01", "modified_usage": "range:min"}
expected = {"modified": {"query": "2004-01-01", "range": "min"}}
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), expected)
def testSortLimit(self):
# the script ignored 'sort_limit'; test to show it no longer does.
request = {"SearchableText": "bar", "sort_on": "foozle", "sort_limit": 50}
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), request)
def testBlacklistedTypes(self):
request = {"SearchableText": "a*"}
registry = getUtility(IRegistry)
search_settings = registry.forInterface(ISearchSchema, prefix="plone")
search_settings.types_not_searched = ("Event",)
qry = self.folder.queryCatalog(request, use_types_blacklist=True)
self.assertTrue("Document" in qry["portal_type"])
self.assertTrue("Event" not in qry["portal_type"])
def testNavigationRoot(self):
request = {"SearchableText": "a*"}
registry = getUtility(IRegistry)
navigation_settings = registry.forInterface(INavigationSchema, prefix="plone")
navigation_settings.root = "/"
qry = self.folder.queryCatalog(request, use_navigation_root=True)
self.assertEqual("/".join(self.portal.getPhysicalPath()), qry["path"])
self.setRoles(("Manager",))
self.portal.invokeFactory("Folder", "foo")
navigation_settings.root = "/foo"
qry = self.folder.queryCatalog(request, use_navigation_root=True)
self.assertEqual("/".join(self.portal.foo.getPhysicalPath()), qry["path"])
def testNavigationRootDoesNotOverrideExplicitPath(self):
request = {"SearchableText": "a*", "path": "/yyy/zzz"}
self.setRoles(("Manager",))
self.portal.invokeFactory("Folder", "foo")
registry = getUtility(IRegistry)
navigation_settings = registry.forInterface(INavigationSchema, prefix="plone")
navigation_settings.root = "/"
qry = self.folder.queryCatalog(request, use_navigation_root=True)
self.assertEqual("/yyy/zzz", qry["path"])
class TestQueryCatalogQuoting(PloneTestCase.PloneTestCase):
"""Test logic quoting features queryCatalog script.
Test function of queryCatalog script, **not** the
functionality of the catalog itself. Therefore, we'll replace
the actual call to the catalog to a dummy routine that just
returns the catalog search dictionary so we can examine what
would be searched.
"""
def dummyCatalog(self, REQUEST=None, **kw):
return kw
def stripStuff(self, query_dict):
# strip portal_types and show_inactive parameter which is
# auto-set with types blacklisting. Useful to simplify test
# assertions when we don't care
if isinstance(query_dict, dict):
for ignore in ["portal_type", "show_inactive"]:
if ignore in query_dict:
del query_dict[ignore]
return query_dict
def afterSetUp(self):
self.portal.portal_catalog.__call__ = self.dummyCatalog
def testQuotingNone(self):
request = {"SearchableText": "Hello Joel"}
expected = request
self.assertEqual(
self.stripStuff(self.folder.queryCatalog(request, quote_logic=1)), expected
)
def testQuotingNotNeeded(self):
request = {"SearchableText": "Hello or Joel"}
expected = request
self.assertEqual(
self.stripStuff(self.folder.queryCatalog(request, quote_logic=1)), expected
)
def testQuotingNotNeededWithNot(self):
request = {"SearchableText": "Hello or not Joel"}
expected = request
self.assertEqual(
self.stripStuff(self.folder.queryCatalog(request, quote_logic=1)), expected
)
def testQuotingRequiredToEscape(self):
request = {"SearchableText": "Hello Joel Or"}
expected = {"SearchableText": 'Hello Joel "Or"'}
self.assertEqual(
self.stripStuff(self.folder.queryCatalog(request, quote_logic=1)), expected
)
def testQuotingRequiredToEscapeOptionOff(self):
request = {"SearchableText": "Hello Joel Or"}
expected = request
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), expected)
def testQuotingWithLeadingNot(self):
request = {"SearchableText": "Not Hello Joel"}
expected = request
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), expected)
def testEmptyItem(self):
request = {"SearchableText": ""}
# queryCatalog will return empty result without calling the catalog
# tool
expected = []
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), expected)
def testEmptyItemShowAll(self):
request = {"SearchableText": ""}
# Catalog gets a blank search, and returns the empty dict
expected = {}
self.assertEqual(
self.stripStuff(self.folder.queryCatalog(request, show_all=1)), expected
)
def testBadCharsAreQuoted(self):
request = {"SearchableText": "context(1)"}
# Catalog gets ( or ) in search and quotes them to avoid parse error
expected = {"SearchableText": 'context"("1")"'}
self.assertEqual(self.stripStuff(self.folder.queryCatalog(request)), expected)
class TestQueryCatalogParseError(PloneTestCase.PloneTestCase):
"""Checks that the queryCatalog script returns an empty result set
in case of ZCTextIndex ParseErrors.
This testcase uses the real catalog, not a stub.
"""
def afterSetUp(self):
self.folder.invokeFactory(
"Document", id="doc", text=RichTextValue("foo bar baz")
)
def testSearchableText(self):
request = {"SearchableText": "foo"}
# We expect a non-empty result set
self.assertTrue(self.portal.queryCatalog(request))
def testParseError(self):
# ZCTextIndex raises ParseError
self.assertRaises(ParseError, self.portal.portal_catalog, SearchableText="-foo")
def testQueryCatalogParseError(self):
request = {"SearchableText": "-foo"}
# ZCTextIndex raises ParseError which translates to empty result
expected = []
self.assertEqual(self.portal.queryCatalog(request), expected)
def testQueryCatalogParseError3050(self):
# http://dev.plone.org/plone/ticket/3050
request = {"SearchableText": "AND"}
# ZCTextIndex raises ParseError which translates to empty result
expected = []
self.assertEqual(self.portal.queryCatalog(request), expected)
AddPortalTopics = "Add portal topics"
class TestSearchForms(PloneTestCase.PloneTestCase):
"""Render all forms related to queryCatalog"""
def testRenderSearchForm(self):
searchView = getMultiAdapter((self.portal, self.app.REQUEST), name="search")
searchView()
def testRenderSearchRSS(self):
searchRssView = getMultiAdapter(
(self.portal, self.app.REQUEST), name="search_rss"
)
searchRssView()
def testSearchGives404WhenDisabled(self):
registry = getUtility(IRegistry)
settings = registry.forInterface(ISiteSyndicationSettings)
settings.search_rss_enabled = False
searchRssView = getMultiAdapter(
(self.portal, self.app.REQUEST), name="search_rss"
)
self.assertRaises(NotFound, searchRssView)