169.2.3
by Aaron Bentley
Implement indexing in terms of elasticsearch. |
1 |
# Copyright 2013 Canonical Ltd. This software is licensed under the
|
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
|
3 |
||
4 |
__metaclass__ = type |
|
5 |
||
6 |
||
197.1.6
by Aaron Bentley
Sort by date_created works. |
7 |
from datetime import datetime |
337.1.2
by Brad Crittenden
Update store bundles to include required data for indexing. |
8 |
from textwrap import dedent |
197.1.6
by Aaron Bentley
Sort by date_created works. |
9 |
|
258.3.8
by Aaron Bentley
search can raise SearchServiceNotAvailable. |
10 |
from pyelasticsearch import ElasticSearch |
251.2.1
by Abel Deuring
sanitize full text search strings to avoid parsing errors |
11 |
from pyelasticsearch.exceptions import ( |
12 |
ElasticHttpError, |
|
13 |
ElasticHttpNotFoundError, |
|
14 |
)
|
|
337.1.2
by Brad Crittenden
Update store bundles to include required data for indexing. |
15 |
import yaml |
169.2.3
by Aaron Bentley
Implement indexing in terms of elasticsearch. |
16 |
|
201.1.1
by Aaron Bentley
Extract store url generation. |
17 |
from charmworld.charmstore import ( |
18 |
get_address, |
|
19 |
make_store_url, |
|
20 |
)
|
|
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
21 |
from charmworld.models import ( |
325.1.4
by Brad Crittenden
Fix lint |
22 |
Bundle, |
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
23 |
Charm, |
337.1.2
by Brad Crittenden
Update store bundles to include required data for indexing. |
24 |
store_bundles, |
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
25 |
)
|
177.1.3
by Aaron Bentley
Implement boosting. |
26 |
from charmworld.search import ( |
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
27 |
BUNDLE, |
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
28 |
CHARM, |
221.2.1
by Aaron Bentley
Add ability to manipulate aliases. |
29 |
ElasticSearchClient, |
344.3.1
by Brad Crittenden
Search bundle's data field. |
30 |
charm_exact_fields, |
31 |
charm_free_text_fields, |
|
221.2.3
by Aaron Bentley
Ensure unique failure for incompatible mapping. |
32 |
IncompatibleMapping, |
221.3.3
by Aaron Bentley
put_mapping raises IndexMissing as needed. |
33 |
IndexMissing, |
258.3.1
by Aaron Bentley
Web site search raises IndexNotReady if document info is unavailable. |
34 |
IndexNotReady, |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
35 |
InvalidCharmType, |
221.2.12
by Aaron Bentley
Rename migrate to update. |
36 |
update, |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
37 |
NegativeLimit, |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
38 |
reindex, |
258.3.8
by Aaron Bentley
search can raise SearchServiceNotAvailable. |
39 |
SearchServiceNotAvailable, |
177.1.6
by Aaron Bentley
Cleanup. |
40 |
)
|
179.3.1
by Aaron Bentley
Remove all mention of Xapian. |
41 |
from charmworld.testing import ( |
42 |
factory, |
|
43 |
temp_index_client, |
|
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
44 |
TestCase, |
179.3.1
by Aaron Bentley
Remove all mention of Xapian. |
45 |
)
|
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
46 |
from charmworld.utils import get_ini |
169.2.3
by Aaron Bentley
Implement indexing in terms of elasticsearch. |
47 |
|
48 |
||
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
49 |
class TestTempIndexClient(TestCase): |
169.2.3
by Aaron Bentley
Implement indexing in terms of elasticsearch. |
50 |
|
51 |
def test_temp_index_client(self): |
|
52 |
with temp_index_client() as client: |
|
53 |
self.assertNotEqual('charms', client.index_name) |
|
54 |
client._client.update_settings(client.index_name, {}) |
|
55 |
with self.assertRaises(ElasticHttpNotFoundError): |
|
56 |
client._client.update_settings(client.index_name, {}) |
|
57 |
||
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
58 |
|
59 |
class TestElasticSearchClient(TestCase): |
|
60 |
||
61 |
def setUp(self): |
|
62 |
super(TestElasticSearchClient, self).setUp() |
|
63 |
self.use_index_client() |
|
64 |
||
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
65 |
def exists_in_index(self, id_, kind=CHARM): |
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
66 |
client = self.index_client |
302.4.10
by Aaron Bentley
In index, put charm document into data attribute, to support ID separation. |
67 |
if kind == CHARM: |
68 |
return client.get(id_) is not None |
|
169.2.3
by Aaron Bentley
Implement indexing in terms of elasticsearch. |
69 |
try: |
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
70 |
result = client._client.get(client.index_name, kind, id_) |
169.2.3
by Aaron Bentley
Implement indexing in terms of elasticsearch. |
71 |
except ElasticHttpNotFoundError: |
72 |
return False |
|
73 |
return result['exists'] |
|
74 |
||
75 |
def test_index_charm(self): |
|
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
76 |
foo_charm = factory.get_charm_json() |
77 |
self.assertFalse(self.exists_in_index(foo_charm['_id'])) |
|
78 |
self.index_client.index_charm(foo_charm) |
|
79 |
self.assertTrue(self.exists_in_index(foo_charm['_id'])) |
|
80 |
||
258.3.14
by Aaron Bentley
Handle max retries exceeded in cstat. |
81 |
def test_index_charm_max_retries_exceeded(self): |
82 |
# The Gopher port should be inactive.
|
|
83 |
client = ElasticSearchClient( |
|
84 |
ElasticSearch(['http://localhost:70']), 'foo') |
|
85 |
with self.assertRaises(SearchServiceNotAvailable): |
|
302.4.10
by Aaron Bentley
In index, put charm document into data attribute, to support ID separation. |
86 |
client.index_charm({'_id': 'id', 'owner': 'foo', 'series': 'bar', |
87 |
'name': 'baz'}) |
|
258.3.14
by Aaron Bentley
Handle max retries exceeded in cstat. |
88 |
|
230.1.1
by Aaron Bentley
Optimize reindexing. |
89 |
def test_index_charms(self): |
90 |
foo_charm = factory.get_charm_json() |
|
91 |
bar_charm = factory.get_charm_json() |
|
92 |
self.assertFalse(self.exists_in_index(foo_charm['_id'])) |
|
93 |
self.assertFalse(self.exists_in_index(bar_charm['_id'])) |
|
94 |
self.index_client.index_charms([foo_charm, bar_charm]) |
|
95 |
self.assertTrue(self.exists_in_index(foo_charm['_id'])) |
|
96 |
self.assertTrue(self.exists_in_index(bar_charm['_id'])) |
|
97 |
||
98 |
def test_index_charms_no_charms(self): |
|
99 |
# Assert that no exception is raised.
|
|
100 |
with self.assertRaises(AssertionError): |
|
101 |
with self.assertRaises(BaseException): |
|
102 |
self.index_client.index_charms([]) |
|
103 |
||
302.4.8
by Aaron Bentley
Add tests for delete_charm. |
104 |
def test_delete_charm(self): |
105 |
charm_data = factory.get_charm_json() |
|
106 |
self.index_client.index_charm(charm_data) |
|
107 |
self.assertTrue(self.exists_in_index(charm_data['_id'])) |
|
302.4.10
by Aaron Bentley
In index, put charm document into data attribute, to support ID separation. |
108 |
self.index_client.delete_charm(charm_data) |
302.4.8
by Aaron Bentley
Add tests for delete_charm. |
109 |
self.assertFalse(self.exists_in_index(charm_data['_id'])) |
110 |
||
288.6.7
by Aaron Bentley
Stop leaving pending aliases behind. |
111 |
def test_update_aliased_None(self): |
112 |
alias = ElasticSearchClient.from_settings(get_ini(), 'foo') |
|
113 |
alias.update_aliased(self.index_client.index_name, []) |
|
114 |
alias.update_aliased(None, []) |
|
115 |
self.assertEqual([self.index_client.index_name], alias.get_aliased()) |
|
116 |
alias.update_aliased(None, [self.index_client.index_name]) |
|
117 |
self.assertEqual([], alias.get_aliased()) |
|
118 |
||
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
119 |
def makeCharm(self, *args, **kwargs): |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
120 |
charm = factory.get_charm_json(*args, **kwargs) |
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
121 |
self.index_client.index_charm(charm) |
122 |
return charm |
|
169.2.7
by Aaron Bentley
Basic test of search functionality. |
123 |
|
325.1.1
by Brad Crittenden
Checkpoint. Tests broken. |
124 |
def makeBundle(self, *args, **kwargs): |
125 |
bundle = factory.get_bundle_data(*args, **kwargs) |
|
126 |
bundle['_id'] = Bundle(bundle).id |
|
127 |
self.index_client.index_bundle(bundle) |
|
128 |
return bundle |
|
129 |
||
169.2.7
by Aaron Bentley
Basic test of search functionality. |
130 |
def test_search(self): |
325.1.1
by Brad Crittenden
Checkpoint. Tests broken. |
131 |
result_keys = ['results', 'search_time', 'result_total', 'matches', |
169.2.16
by Aaron Bentley
Cleanup and test fixes. |
132 |
'matches_human_readable_estimate'] |
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
133 |
client = self.index_client |
283.1.3
by Abel Deuring
ElasticSearchClient.search(): Return Charm objects instead of simple dictionaries. |
134 |
foo_charm = Charm(self.makeCharm()) |
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
135 |
results = client.search('foo') |
136 |
self.assertItemsEqual(result_keys, results.keys()) |
|
325.1.1
by Brad Crittenden
Checkpoint. Tests broken. |
137 |
self.assertEqual([], results['results']['charm']) |
138 |
results = client.search(foo_charm.name)['results']['charm'] |
|
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
139 |
self.assertEqual(1, len(results)) |
140 |
self.assertEqual(foo_charm, results[0]['data']) |
|
141 |
# The absolute value of weight is not defined, so we just ensure
|
|
142 |
# it's present.
|
|
143 |
self.assertItemsEqual(['data', 'weight'], results[0].keys()) |
|
177.1.1
by Aaron Bentley
Ensure only specific fields are searched. |
144 |
|
325.1.1
by Brad Crittenden
Checkpoint. Tests broken. |
145 |
def test_search_bundle(self): |
146 |
bundle = Bundle(self.makeBundle()) |
|
147 |
client = self.index_client |
|
344.3.1
by Brad Crittenden
Search bundle's data field. |
148 |
results = client.search(bundle.name)['results'] |
149 |
self.assertEqual(1, len(results['bundle'])) |
|
150 |
self.assertEqual(bundle, results['bundle'][0]['data']) |
|
325.1.1
by Brad Crittenden
Checkpoint. Tests broken. |
151 |
|
367.1.1
by Brad Crittenden
Search for charm names within the services dict. |
152 |
def _setup_store_bundles(self): |
337.1.4
by Brad Crittenden
Broken version where basket is stored with owner as a prefix. |
153 |
_id = '~abentley/wordpress-basket/5/wordpress-stage' |
337.1.2
by Brad Crittenden
Update store bundles to include required data for indexing. |
154 |
deployer_config = dedent("""\ |
155 |
wordpress-stage:
|
|
156 |
series: precise
|
|
157 |
services:
|
|
158 |
blog:
|
|
159 |
charm: cs:precise/wordpress
|
|
160 |
constraints: mem=2
|
|
161 |
options:
|
|
162 |
tuning: optimized
|
|
163 |
engine: apache
|
|
367.1.1
by Brad Crittenden
Search for charm names within the services dict. |
164 |
db:
|
165 |
charm: cs:precise/mysql
|
|
337.1.2
by Brad Crittenden
Update store bundles to include required data for indexing. |
166 |
""") |
167 |
parsed = yaml.safe_load(deployer_config) |
|
337.1.4
by Brad Crittenden
Broken version where basket is stored with owner as a prefix. |
168 |
store_bundles(None, parsed, 'abentley', 'wordpress-basket/5', |
367.1.1
by Brad Crittenden
Search for charm names within the services dict. |
169 |
index_client=self.index_client) |
170 |
return _id |
|
171 |
||
172 |
def test_store_bundles_bundle_name_indexed(self): |
|
173 |
# Ensure that bundles stored into the index via the `store_bundles`
|
|
174 |
# function are searchable via the index.
|
|
368.1.3
by Brad Crittenden
De-lint |
175 |
_id = self._setup_store_bundles() |
367.1.1
by Brad Crittenden
Search for charm names within the services dict. |
176 |
|
337.1.2
by Brad Crittenden
Update store bundles to include required data for indexing. |
177 |
# Bundle name is indexed.
|
367.1.1
by Brad Crittenden
Search for charm names within the services dict. |
178 |
results = self.index_client.search( |
179 |
'wordpress-stage')['results']['bundle'] |
|
337.1.2
by Brad Crittenden
Update store bundles to include required data for indexing. |
180 |
self.assertEqual(1, len(results)) |
181 |
self.assertEqual(_id, results[0]['data'].id) |
|
367.1.1
by Brad Crittenden
Search for charm names within the services dict. |
182 |
|
183 |
def test_store_bundles_series_indexed(self): |
|
368.1.3
by Brad Crittenden
De-lint |
184 |
_id = self._setup_store_bundles() |
337.1.2
by Brad Crittenden
Update store bundles to include required data for indexing. |
185 |
# Series is indexed.
|
367.1.1
by Brad Crittenden
Search for charm names within the services dict. |
186 |
results = self.index_client.search('precise')['results']['bundle'] |
337.1.2
by Brad Crittenden
Update store bundles to include required data for indexing. |
187 |
self.assertEqual(1, len(results)) |
188 |
self.assertEqual(_id, results[0]['data'].id) |
|
367.1.1
by Brad Crittenden
Search for charm names within the services dict. |
189 |
|
368.1.3
by Brad Crittenden
De-lint |
190 |
def test_store_bundles_owner_indexed(self): |
191 |
_id = self._setup_store_bundles() |
|
337.1.2
by Brad Crittenden
Update store bundles to include required data for indexing. |
192 |
# Owner is indexed.
|
367.1.1
by Brad Crittenden
Search for charm names within the services dict. |
193 |
results = self.index_client.search('abentley')['results']['bundle'] |
194 |
self.assertEqual(1, len(results)) |
|
195 |
self.assertEqual(_id, results[0]['data'].id) |
|
196 |
||
368.1.3
by Brad Crittenden
De-lint |
197 |
def test_store_bundles_charm_names_indexed(self): |
198 |
_id = self._setup_store_bundles() |
|
367.1.1
by Brad Crittenden
Search for charm names within the services dict. |
199 |
# Charm names within services are index.
|
200 |
results = self.index_client.search('mysql')['results']['bundle'] |
|
201 |
self.assertEqual(1, len(results)) |
|
202 |
self.assertEqual(_id, results[0]['data'].id) |
|
203 |
||
325.1.1
by Brad Crittenden
Checkpoint. Tests broken. |
204 |
def test_search_charms_and_bundles_same_name(self): |
205 |
charm = Charm(self.makeCharm(name='mozilla')) |
|
206 |
bundle = Bundle(self.makeBundle(name='mozilla')) |
|
207 |
client = self.index_client |
|
208 |
search_results = client.search('mozilla') |
|
209 |
results = search_results['results'] |
|
210 |
self.assertEqual(2, len(results)) |
|
211 |
self.assertEqual(2, search_results['result_total']) |
|
212 |
self.assertEqual(2, search_results['matches']) |
|
213 |
self.assertEqual(1, len(results['charm'])) |
|
214 |
self.assertEqual(1, len(results['bundle'])) |
|
215 |
self.assertEqual(charm, results['charm'][0]['data']) |
|
216 |
self.assertEqual(bundle, results['bundle'][0]['data']) |
|
217 |
||
218 |
def test_search_charms_and_bundles_description(self): |
|
219 |
charm = Charm(self.makeCharm(description='a mozilla charm')) |
|
220 |
bundle = Bundle(self.makeBundle(description='a mozilla bundle')) |
|
221 |
client = self.index_client |
|
222 |
search_results = client.search('mozilla') |
|
223 |
results = search_results['results'] |
|
224 |
self.assertEqual(2, len(results)) |
|
225 |
self.assertEqual(2, search_results['result_total']) |
|
226 |
self.assertEqual(2, search_results['matches']) |
|
227 |
self.assertEqual(1, len(results['charm'])) |
|
228 |
self.assertEqual(1, len(results['bundle'])) |
|
229 |
self.assertEqual(charm, results['charm'][0]['data']) |
|
230 |
self.assertEqual(bundle, results['bundle'][0]['data']) |
|
231 |
||
258.3.1
by Aaron Bentley
Web site search raises IndexNotReady if document info is unavailable. |
232 |
def test_search_not_ready(self): |
233 |
||
234 |
class FakeElasticSearch: |
|
235 |
||
236 |
@staticmethod
|
|
237 |
def status(index): |
|
238 |
return {'indices': {'foo': {}}} |
|
239 |
||
240 |
@staticmethod
|
|
241 |
def aliases(): |
|
242 |
return {} |
|
243 |
||
244 |
client = ElasticSearchClient(FakeElasticSearch, 'foo') |
|
245 |
with self.assertRaises(IndexNotReady): |
|
246 |
client.search('bar') |
|
247 |
||
258.3.8
by Aaron Bentley
search can raise SearchServiceNotAvailable. |
248 |
def test_max_retries_exceeded_search(self): |
249 |
# The Gopher port should be inactive.
|
|
250 |
client = ElasticSearchClient( |
|
251 |
ElasticSearch(['http://localhost:70']), 'foo') |
|
252 |
with self.assertRaises(SearchServiceNotAvailable): |
|
253 |
client.search('bar') |
|
254 |
||
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
255 |
def search_ids(self, terms): |
256 |
client = self.index_client |
|
325.1.1
by Brad Crittenden
Checkpoint. Tests broken. |
257 |
hits = client.search(terms)['results']['charm'] |
258 |
return [hit['data']._id for hit in hits] |
|
177.1.1
by Aaron Bentley
Ensure only specific fields are searched. |
259 |
|
260 |
def test_search_matches_on_search_terms(self): |
|
344.3.1
by Brad Crittenden
Search bundle's data field. |
261 |
fields = charm_free_text_fields.keys() + charm_exact_fields |
177.1.4
by Aaron Bentley
All desired fields are searchable and tested. |
262 |
fields = [field.split('.')[0] for field in fields] |
221.4.5
by Aaron Bentley
Fix failing test. |
263 |
charm = factory.get_charm_json(categories=['unknown']) |
177.1.4
by Aaron Bentley
All desired fields are searchable and tested. |
264 |
# Ensure all fields have unique values.
|
340.1.1
by Abel Deuring
do not try to use non-existent value charm['store_data']['address'] in templates; property Charm.address added. |
265 |
charm['address'] = 'cs:bogus-address' |
177.1.1
by Aaron Bentley
Ensure only specific fields are searched. |
266 |
charm['owner'] = 'steve' |
267 |
charm['series'] = 'grumpy' |
|
268 |
charm['name'] = 'arthur' |
|
269 |
charm['label'] = 'organic' |
|
270 |
charm['bname'] = 'my-dev-branch' |
|
177.1.3
by Aaron Bentley
Implement boosting. |
271 |
charm['i_provides'] = ['provisions'] |
177.1.4
by Aaron Bentley
All desired fields are searchable and tested. |
272 |
charm['provides']['website']['interface'] = 'gopher' |
273 |
charm['requires']['database']['interface'] = 'reiserfs' |
|
177.1.1
by Aaron Bentley
Ensure only specific fields are searched. |
274 |
charm['_id'] = 'asdf' |
275 |
# Best-effort ensure there's no overlap between search terms
|
|
276 |
items = charm.items() |
|
277 |
for num, (key, value) in enumerate(items): |
|
278 |
if not isinstance(value, basestring): |
|
279 |
continue
|
|
280 |
for key2, value2 in items[num + 1:]: |
|
281 |
if not isinstance(value2, basestring): |
|
282 |
continue
|
|
283 |
self.assertNotIn(value, value2, '%s in %s' % (key, key2)) |
|
284 |
self.assertNotIn(value2, value, '%s in %s' % (key2, key)) |
|
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
285 |
self.index_client.index_charm(charm) |
286 |
for key, query in items: |
|
225.2.1
by Abel Deuring
JenkinsIngestJob.run(): Better check if a charm is promulgated. |
287 |
# Skip ints and lists.
|
288.2.1
by Rick Harding
It's alive and tests pass |
288 |
if key in ('revision', 'downloads_in_past_30_days', 'downloads'): |
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
289 |
continue
|
221.4.5
by Aaron Bentley
Fix failing test. |
290 |
elif key in ('hooks', 'i_requires', 'i_provides', 'categories'): |
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
291 |
query = query[0] |
292 |
elif key == 'changes': |
|
293 |
query = query[0]['message'] |
|
294 |
elif key in ('provides', 'requires'): |
|
295 |
query = query.values()[0].values()[0] |
|
296 |
elif key == 'config': |
|
284.1.3
by Aaron Bentley
Start using new internal format for options. |
297 |
query = query.values()[0][0]['description'] |
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
298 |
elif key == 'store_data': |
299 |
query = query['digest'] |
|
300 |
elif key == 'files': |
|
301 |
query = query['readme']['filename'] |
|
302 |
elif key in ('first_change', 'last_change'): |
|
303 |
query = query['message'] |
|
304 |
elif key == 'proof': |
|
305 |
query = query['w'][0] |
|
197.1.14
by Aaron Bentley
Fix failing tests. |
306 |
self.assertIsInstance(query, (basestring, bool)) |
307 |
if isinstance(query, basestring): |
|
308 |
query = '"%s"' % self.index_client.escape_query(query) |
|
309 |
else: |
|
310 |
self.assertIsInstance(query, bool) |
|
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
311 |
ids = self.search_ids(query) |
312 |
if key in fields: |
|
313 |
self.assertEqual(1, len(ids), 'No hit for %s' % key) |
|
314 |
else: |
|
315 |
self.assertEqual(0, len(ids), 'Undesired hit on %s' % key) |
|
179.1.1
by Aaron Bentley
Boost official charms. |
316 |
|
317 |
def test_official_charms_boost(self): |
|
318 |
"""An official version of a charm rates 10x higher."""
|
|
225.2.18
by Abel Deuring
ElasticSearch queries: Do not assuma that charmers are the only owners of promulgated charms. |
319 |
charm = factory.get_charm_json(promulgated=True, owner='foo') |
179.1.1
by Aaron Bentley
Boost official charms. |
320 |
official_id = charm['_id'] |
321 |
unofficial_id = official_id + '-1' |
|
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
322 |
client = self.index_client |
323 |
client.index_charm(charm) |
|
324 |
charm['owner'] = 'jrandom' |
|
325 |
charm['_id'] = unofficial_id |
|
225.2.18
by Abel Deuring
ElasticSearch queries: Do not assuma that charmers are the only owners of promulgated charms. |
326 |
charm['store_url'] = get_address(charm, short=False) |
182.2.17
by Aaron Bentley
Revamp TestElasticSearchClient to use use_index_client() |
327 |
client.index_charm(charm) |
325.1.1
by Brad Crittenden
Checkpoint. Tests broken. |
328 |
results = client.search('terminal')['results']['charm'] |
283.1.3
by Abel Deuring
ElasticSearchClient.search(): Return Charm objects instead of simple dictionaries. |
329 |
by_id = dict((result['data']._id, result) for result in results) |
179.1.1
by Aaron Bentley
Boost official charms. |
330 |
self.assertAlmostEqual(by_id[official_id]['weight'], |
331 |
by_id[unofficial_id]['weight'] * 10) |
|
182.2.18
by Aaron Bentley
Get api_search fully under test. |
332 |
|
201.1.3
by Aaron Bentley
Unpromulgated charms are not boosted. |
333 |
def test_unreviewed_charmers_not_boosted(self): |
334 |
"""Unpromulgated charms are not boosted even if owned by charmers."""
|
|
335 |
charm = factory.get_charm_json(promulgated=False) |
|
336 |
charmers_id = charm['_id'] |
|
337 |
jrandom_id = charmers_id + '-1' |
|
338 |
client = self.index_client |
|
339 |
client.index_charm(charm) |
|
340 |
charm['owner'] = 'jrandom' |
|
341 |
charm['_id'] = jrandom_id |
|
342 |
client.index_charm(charm) |
|
325.1.1
by Brad Crittenden
Checkpoint. Tests broken. |
343 |
results = client.search('terminal')['results']['charm'] |
283.1.3
by Abel Deuring
ElasticSearchClient.search(): Return Charm objects instead of simple dictionaries. |
344 |
by_id = dict((result['data']._id, result) for result in results) |
201.1.3
by Aaron Bentley
Unpromulgated charms are not boosted. |
345 |
self.assertAlmostEqual(by_id[charmers_id]['weight'], |
346 |
by_id[jrandom_id]['weight']) |
|
347 |
||
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
348 |
def get_charm_bundle_test_data(self): |
225.2.2
by Abel Deuring
factory.get_charm_json(): Proper handling of the promulgated parameter. |
349 |
one = self.makeCharm(name='name1', promulgated=True) |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
350 |
maintainer2 = 'jrandom@example.com (J. Random Hacker)' |
351 |
provides2 = {'cookie-factory': { |
|
352 |
'interface': 'imap', |
|
353 |
}}
|
|
354 |
requires2 = {'postal-service': { |
|
355 |
'interface': 'envelope', |
|
356 |
}}
|
|
357 |
options2 = { |
|
358 |
'foobar': { |
|
359 |
'type': 'str', |
|
360 |
'default': 'baz', |
|
361 |
'description': 'quxx', |
|
362 |
},
|
|
363 |
}
|
|
364 |
files2 = { |
|
365 |
'readme': { |
|
366 |
'filename': 'README.md', |
|
367 |
'subdir': '', |
|
368 |
},
|
|
369 |
'config-changed': { |
|
370 |
'filename': 'config-changed', |
|
371 |
'subdir': 'hooks', |
|
372 |
},
|
|
373 |
}
|
|
374 |
two = self.makeCharm(owner='jrandom', revision=3, |
|
375 |
series='warty', summary='Charm two', |
|
376 |
name='name2', revno=13, |
|
377 |
maintainer=maintainer2, |
|
378 |
commit_message='message2', |
|
379 |
provides=provides2, |
|
380 |
requires=requires2, options=options2, |
|
381 |
files=files2, subordinate=True) |
|
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
382 |
bundle = self.makeBundle(name='name3') |
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
383 |
return one, two, bundle |
384 |
||
385 |
def test_api_search_all(self): |
|
362.1.2
by Curtis Hovey
Always include the doctype with api_search results when the doctype is not specified. |
386 |
# With no parameters, all charms and bundles are returned.
|
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
387 |
one, two, bundle = self.get_charm_bundle_test_data() |
362.1.6
by Curtis Hovey
DRY tests. |
388 |
result = self.get_charms_and_bundles(doctype=None) |
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
389 |
self.assertEqual(3, len(result)) |
201.1.1
by Aaron Bentley
Extract store url generation. |
390 |
one_url = make_store_url(1, get_address(one, short=True)) |
182.2.19
by Aaron Bentley
Cleanup. |
391 |
self.assertEqual(one, result[0]) |
201.1.1
by Aaron Bentley
Extract store url generation. |
392 |
two_url = make_store_url(1, get_address(two, short=False)) |
182.2.19
by Aaron Bentley
Cleanup. |
393 |
self.assertEqual(two, result[1]) |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
394 |
self.assertNotEqual(one['_id'], two['_id']) |
395 |
self.assertNotEqual(one_url, two_url) |
|
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
396 |
self.assertEqual(bundle, result[2]) |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
397 |
|
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
398 |
def test_api_search_charms_only(self): |
399 |
"""With no parameters, all charms are returned."""
|
|
400 |
one, two, bundle = self.get_charm_bundle_test_data() |
|
401 |
result = self.get_charms_and_bundles(doctype=CHARM) |
|
402 |
self.assertEqual(2, len(result)) |
|
362.1.6
by Curtis Hovey
DRY tests. |
403 |
self.assertEqual(one, result[0]) |
404 |
self.assertEqual(two, result[1]) |
|
362.1.7
by Curtis Hovey
DRY tests. |
405 |
self.assertNotEqual(one['_id'], two['_id']) |
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
406 |
|
407 |
def test_api_search_bundles_only(self): |
|
332.1.2
by Brad Crittenden
Fix doctype cut-n-paste error. |
408 |
"""Passing doctype of 'bundles' retrieves only bundles."""
|
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
409 |
one, two, bundle = self.get_charm_bundle_test_data() |
410 |
result = self.get_charms_and_bundles(doctype=BUNDLE) |
|
411 |
self.assertEqual(1, len(result)) |
|
362.1.6
by Curtis Hovey
DRY tests. |
412 |
self.assertEqual(bundle, result[0]) |
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
413 |
|
298.1.1
by Aaron Bentley
Autocomplete flag switches to matching on names only. |
414 |
def test_autocomplete_matches_on_names(self): |
415 |
matching_charm = self.makeCharm(name='foo') |
|
416 |
differing_charm = self.makeCharm(name='bar') |
|
344.3.1
by Brad Crittenden
Search bundle's data field. |
417 |
for field in charm_free_text_fields: |
298.1.1
by Aaron Bentley
Autocomplete flag switches to matching on names only. |
418 |
if '.' in field or field == 'name': |
419 |
continue
|
|
420 |
differing_charm[field] = 'foo' |
|
344.3.1
by Brad Crittenden
Search bundle's data field. |
421 |
for field in charm_exact_fields: |
298.1.1
by Aaron Bentley
Autocomplete flag switches to matching on names only. |
422 |
differing_charm[field] = 'foo' |
423 |
self.index_client.index_charm(differing_charm) |
|
362.1.7
by Curtis Hovey
DRY tests. |
424 |
result = self.index_client.api_search('foo', autocomplete=True) |
425 |
ids = [r['data']['_id'] for r in result] |
|
298.1.1
by Aaron Bentley
Autocomplete flag switches to matching on names only. |
426 |
self.assertEqual([matching_charm['_id']], ids) |
427 |
||
298.1.2
by Aaron Bentley
Autocomplete does prefix matching. |
428 |
def test_autocomplete_matches_on_prefix(self): |
429 |
charm1 = self.makeCharm(name='foo') |
|
430 |
charm2 = self.makeCharm(name='foobar') |
|
304.3.1
by Aaron Bentley
Fix lint. |
431 |
self.makeCharm(name='barfoo') |
362.1.4
by Curtis Hovey
Always return the doctype with api_search. |
432 |
result = self.index_client.api_search('foo', autocomplete=True) |
433 |
ids = [r['data']['_id'] for r in result] |
|
298.1.2
by Aaron Bentley
Autocomplete does prefix matching. |
434 |
self.assertItemsEqual([charm1['_id'], charm2['_id']], ids) |
435 |
||
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
436 |
def get_charms_and_bundles(self, text='', type_=None, doctype=CHARM, |
325.1.7
by Brad Crittenden
Fixed valid charm search. Tests working but not complete. |
437 |
*args, **kwargs): |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
438 |
filters = dict((key, [value]) for key, value in kwargs.items()) |
439 |
if type_ is not None and isinstance(type_, basestring): |
|
440 |
type_ = [type_] |
|
362.1.6
by Curtis Hovey
DRY tests. |
441 |
result = self.index_client.api_search( |
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
442 |
text, filters, type_, doctype=doctype) |
362.1.6
by Curtis Hovey
DRY tests. |
443 |
charms_and_bundles = [r['data'] for r in result] |
444 |
return sorted(charms_and_bundles, key=lambda x: x['name']) |
|
182.2.18
by Aaron Bentley
Get api_search fully under test. |
445 |
|
446 |
def _test_filter(self, filter_str, make_charm=None): |
|
447 |
if make_charm is None: |
|
448 |
def make_charm(value, warty=False): |
|
449 |
kwargs = {filter_str: value} |
|
450 |
if filter_str != 'series' and warty: |
|
451 |
kwargs['series'] = 'warty' |
|
452 |
return self.makeCharm(**kwargs) |
|
453 |
one = make_charm('common') |
|
454 |
self.index_client.index_charm(one) |
|
455 |
two = make_charm('common', warty=True) |
|
456 |
self.index_client.index_charm(two) |
|
457 |
three = make_charm('different') |
|
458 |
self.index_client.index_charm(three) |
|
459 |
four = make_charm('different-again') |
|
460 |
self.index_client.index_charm(four) |
|
325.1.7
by Brad Crittenden
Fixed valid charm search. Tests working but not complete. |
461 |
result = self.get_charms_and_bundles(**{filter_str: 'common'}) |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
462 |
self.assertEqual(2, len(result)) |
463 |
self.assertEqual(set([one['store_url'], two['store_url']]), |
|
464 |
set(charm['store_url'] for charm in result)) |
|
325.1.7
by Brad Crittenden
Fixed valid charm search. Tests working but not complete. |
465 |
result = self.get_charms_and_bundles(**{filter_str: 'different'}) |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
466 |
self.assertEqual(set([three['store_url']]), |
467 |
set(charm['store_url'] for charm in result)) |
|
468 |
self.assertEqual(1, len(result)) |
|
325.1.7
by Brad Crittenden
Fixed valid charm search. Tests working but not complete. |
469 |
result = self.get_charms_and_bundles( |
470 |
**{filter_str: ['different', 'common']}) |
|
182.2.18
by Aaron Bentley
Get api_search fully under test. |
471 |
self.assertEqual(3, len(result)) |
472 |
||
473 |
def test_charms_name_filter(self): |
|
474 |
"""Charms can be filtered by name."""
|
|
475 |
self._test_filter('name') |
|
476 |
||
477 |
def test_charms_series_filter(self): |
|
478 |
"""Charms can be filtered by series."""
|
|
479 |
self._test_filter('series') |
|
480 |
||
481 |
def test_charms_owner_filter(self): |
|
482 |
"""Charms can be filtered by owner."""
|
|
483 |
self._test_filter('owner') |
|
484 |
||
485 |
def test_charms_provides_filter(self): |
|
486 |
"""Charms can be filtered by provided interfaces."""
|
|
487 |
def make_charm(value, warty=False): |
|
488 |
provides = {'foo': {'interface': value}} |
|
489 |
return self.makeCharm(provides=provides) |
|
490 |
self._test_filter('i_provides', make_charm) |
|
491 |
||
492 |
def test_charms_requires_filter(self): |
|
493 |
"""Charms can be filtered by required interfaces."""
|
|
494 |
def make_charm(value, warty=False): |
|
495 |
requires = {'foo': {'interface': value}} |
|
496 |
return self.makeCharm(requires=requires) |
|
497 |
self._test_filter('i_requires', make_charm) |
|
498 |
||
499 |
def test_charms_type_filter(self): |
|
500 |
"""Filtering by type works as well as possible.
|
|
501 |
||
315.1.1
by Brad Crittenden
Add doctype to charms and bundles. Introduce API3, but don't use it. |
502 |
Charms are considered 'approved' if they are promulgated,
|
225.2.18
by Abel Deuring
ElasticSearch queries: Do not assuma that charmers are the only owners of promulgated charms. |
503 |
'community' otherwise. 'environment' causes no charms
|
201.1.5
by Aaron Bentley
Update API to consider whether charms are promulgated. |
504 |
to be returned.
|
182.2.18
by Aaron Bentley
Get api_search fully under test. |
505 |
"""
|
362.1.6
by Curtis Hovey
DRY tests. |
506 |
self.makeCharm(owner='alice', name='ant', promulgated=True) |
507 |
self.makeCharm(owner='steve', name='bat') |
|
325.1.7
by Brad Crittenden
Fixed valid charm search. Tests working but not complete. |
508 |
result = self.get_charms_and_bundles(type_='community') |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
509 |
self.assertEqual(['steve'], [charm['owner'] for charm in result]) |
325.1.7
by Brad Crittenden
Fixed valid charm search. Tests working but not complete. |
510 |
result = self.get_charms_and_bundles(type_='approved') |
225.2.18
by Abel Deuring
ElasticSearch queries: Do not assuma that charmers are the only owners of promulgated charms. |
511 |
self.assertEqual(['alice'], [charm['owner'] for charm in result]) |
325.1.7
by Brad Crittenden
Fixed valid charm search. Tests working but not complete. |
512 |
result = self.get_charms_and_bundles(type_='environment') |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
513 |
self.assertEqual([], [charm['owner'] for charm in result]) |
514 |
with self.assertRaises(InvalidCharmType): |
|
325.1.7
by Brad Crittenden
Fixed valid charm search. Tests working but not complete. |
515 |
self.get_charms_and_bundles(type_='foo') |
516 |
result = self.get_charms_and_bundles(type_=['approved', 'community']) |
|
225.2.18
by Abel Deuring
ElasticSearch queries: Do not assuma that charmers are the only owners of promulgated charms. |
517 |
self.assertEqual(['alice', 'steve'], |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
518 |
[charm['owner'] for charm in result]) |
519 |
||
201.1.4
by Aaron Bentley
Search filters consider promulgation for charm type. |
520 |
def test_type_search_considers_promulgated(self): |
521 |
reviewed = self.makeCharm(promulgated=True)['_id'] |
|
203.1.4
by Aaron Bentley
Invoke update_date_created directly. |
522 |
self.makeCharm(promulgated=False) |
325.1.7
by Brad Crittenden
Fixed valid charm search. Tests working but not complete. |
523 |
results = self.get_charms_and_bundles(type_=['approved']) |
201.1.4
by Aaron Bentley
Search filters consider promulgation for charm type. |
524 |
self.assertEqual([reviewed], [result['_id'] for result in results]) |
525 |
||
182.2.18
by Aaron Bentley
Get api_search fully under test. |
526 |
def test_charms_text_search(self): |
527 |
abc_charm = self.makeCharm(summary='abc def') |
|
182.2.19
by Aaron Bentley
Cleanup. |
528 |
self.makeCharm(summary='ghi jkl') |
325.1.7
by Brad Crittenden
Fixed valid charm search. Tests working but not complete. |
529 |
charms = self.get_charms_and_bundles(text='def') |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
530 |
self.assertEqual(1, len(charms)) |
362.1.6
by Curtis Hovey
DRY tests. |
531 |
self.assertEqual(abc_charm['_id'], charms[0]['_id']) |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
532 |
|
533 |
def test_charms_text_search_respects_filters(self): |
|
225.2.2
by Abel Deuring
factory.get_charm_json(): Proper handling of the promulgated parameter. |
534 |
official = self.makeCharm(summary='abc def', promulgated=True) |
535 |
self.makeCharm(promulgated=True) |
|
182.2.19
by Aaron Bentley
Cleanup. |
536 |
self.makeCharm(summary='abc def', owner='steve') |
537 |
self.makeCharm(owner='steve') |
|
325.1.7
by Brad Crittenden
Fixed valid charm search. Tests working but not complete. |
538 |
result = self.get_charms_and_bundles(type_=['approved'], text='def') |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
539 |
self.assertEqual([official['_id']], |
362.1.6
by Curtis Hovey
DRY tests. |
540 |
[charm['_id'] for charm in result]) |
182.2.18
by Aaron Bentley
Get api_search fully under test. |
541 |
|
542 |
def test_charms_limit(self): |
|
543 |
"""Limit provides a maximum for charm numbers."""
|
|
544 |
result = self.index_client.api_search('', {}, None, 6) |
|
545 |
self.assertEqual(0, len(result)) |
|
546 |
self.makeCharm() |
|
547 |
self.makeCharm(owner='jrandom') |
|
548 |
result = self.index_client.api_search('', {}, None, 6) |
|
549 |
self.assertEqual(2, len(result)) |
|
550 |
result = self.index_client.api_search('', {}, None, 1) |
|
551 |
self.assertEqual(1, len(result)) |
|
552 |
result = self.index_client.api_search('', {}, None, 0) |
|
553 |
self.assertEqual(0, len(result)) |
|
554 |
with self.assertRaises(NegativeLimit): |
|
555 |
result = self.index_client.api_search('', {}, None, -1) |
|
188.1.1
by Abel Deuring
return only charms without errors in api_search() by default. |
556 |
|
362.1.7
by Curtis Hovey
DRY tests. |
557 |
def test_api_search_with_store_error(self): |
188.1.2
by Abel Deuring
return only charms without errors in search() by default. |
558 |
"""api_search() does not return charms withs errors by default."""
|
188.1.1
by Abel Deuring
return only charms without errors in api_search() by default. |
559 |
self.makeCharm(name='evil-charm', charm_error=True) |
560 |
self.makeCharm(name='good-charm') |
|
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
561 |
self.makeBundle(name='great-bundle') |
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
562 |
result = self.index_client.api_search('', {}, None, doctype=None) |
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
563 |
self.assertEqual(2, len(result)) |
362.1.2
by Curtis Hovey
Always include the doctype with api_search results when the doctype is not specified. |
564 |
names = set(charm['data']['name'] for charm in result) |
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
565 |
self.assertEqual(set(('good-charm', 'great-bundle')), names) |
188.1.1
by Abel Deuring
return only charms without errors in api_search() by default. |
566 |
# Charms with error are included in the search result
|
325.1.9
by Brad Crittenden
Reverted the changes to search_json to restore backwards compatability. Made the other readability and style changes from review. |
567 |
# if the parameter valid_only is set to False. Bundles are
|
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
568 |
# always included.
|
188.1.1
by Abel Deuring
return only charms without errors in api_search() by default. |
569 |
result = self.index_client.api_search( |
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
570 |
'', {}, None, valid_only=False, doctype=None) |
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
571 |
self.assertEqual(3, len(result)) |
362.1.2
by Curtis Hovey
Always include the doctype with api_search results when the doctype is not specified. |
572 |
names = set(charm['data']['name'] for charm in result) |
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
573 |
self.assertEqual( |
574 |
set(('good-charm', 'evil-charm', 'great-bundle')), names) |
|
188.1.2
by Abel Deuring
return only charms without errors in search() by default. |
575 |
|
362.1.7
by Curtis Hovey
DRY tests. |
576 |
def test_search_with_store_error(self): |
188.1.2
by Abel Deuring
return only charms without errors in search() by default. |
577 |
"""search() does not return charms withs errors by default."""
|
578 |
self.makeCharm(name='evil-charm', owner='foo', charm_error=True) |
|
579 |
self.makeCharm(name='good-charm', owner='foo') |
|
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
580 |
self.makeBundle(name='great-bundle', owner='foo') |
188.1.2
by Abel Deuring
return only charms without errors in search() by default. |
581 |
result = self.index_client.search('foo') |
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
582 |
self.assertEqual(2, result['matches']) |
325.1.1
by Brad Crittenden
Checkpoint. Tests broken. |
583 |
data = result['results']['charm'][0]['data'] |
584 |
self.assertEqual('good-charm', data.name) |
|
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
585 |
data = result['results']['bundle'][0]['data'] |
586 |
self.assertEqual('great-bundle', data.name) |
|
188.1.2
by Abel Deuring
return only charms without errors in search() by default. |
587 |
# Charms with error are included in the search result
|
325.1.9
by Brad Crittenden
Reverted the changes to search_json to restore backwards compatability. Made the other readability and style changes from review. |
588 |
# if the parameter valid_only is set to False.
|
589 |
result = self.index_client.search('foo', valid_only=False) |
|
325.1.8
by Brad Crittenden
Update tests for bundle searching with valid_items_only |
590 |
self.assertEqual(3, result['matches']) |
591 |
names = set(e['data'].name for e in result['results']['charm']) |
|
592 |
names.update(set(e['data'].name for e in result['results']['bundle'])) |
|
593 |
self.assertEqual( |
|
594 |
set(('good-charm', 'evil-charm', 'great-bundle')), names) |
|
197.1.4
by Aaron Bentley
Implement searching by popularity. |
595 |
|
213.1.3
by Aaron Bentley
Ensure search excludes error charms. |
596 |
def test_search_charm_with_error(self): |
286.2.3
by Abel Deuring
fix obvious failure of test_search_charm_with_error() |
597 |
"""searches include charms with processing errors."""
|
283.1.3
by Abel Deuring
ElasticSearchClient.search(): Return Charm objects instead of simple dictionaries. |
598 |
charm = Charm(self.makeCharm()) |
599 |
result = self.index_client.search(charm.name) |
|
325.1.1
by Brad Crittenden
Checkpoint. Tests broken. |
600 |
self.assertEqual(charm, result['results']['charm'][0]['data']) |
283.1.3
by Abel Deuring
ElasticSearchClient.search(): Return Charm objects instead of simple dictionaries. |
601 |
charm._representation['error'] = {'error': 'error text'} |
602 |
self.index_client.index_charm(charm._representation) |
|
603 |
result = self.index_client.search(charm.name) |
|
325.1.1
by Brad Crittenden
Checkpoint. Tests broken. |
604 |
self.assertEqual(charm, result['results']['charm'][0]['data']) |
213.1.3
by Aaron Bentley
Ensure search excludes error charms. |
605 |
|
213.1.5
by Aaron Bentley
Test API search. |
606 |
def test_api_search_charm_with_error(self): |
286.2.2
by Abel Deuring
test that charms with processing errors are included by find_charms() and search() |
607 |
"""Search results include charms with processing errors."""
|
213.1.5
by Aaron Bentley
Test API search. |
608 |
charm = self.makeCharm() |
286.2.2
by Abel Deuring
test that charms with processing errors are included by find_charms() and search() |
609 |
charm['error'] = {'error': 'error text'} |
213.1.5
by Aaron Bentley
Test API search. |
610 |
self.index_client.index_charm(charm) |
611 |
result = self.index_client.api_search(charm['name']) |
|
362.1.8
by Curtis Hovey
Remove extra lines. |
612 |
self.assertEqual([charm], [r['data'] for r in result]) |
213.1.5
by Aaron Bentley
Test API search. |
613 |
|
197.1.4
by Aaron Bentley
Implement searching by popularity. |
614 |
def test_search_charm_sort_most_downloaded(self): |
615 |
"""Specifying 'downloaded' sorts results appropriately."""
|
|
288.2.1
by Rick Harding
It's alive and tests pass |
616 |
self.makeCharm(name='popular1', downloads=3) |
302.5.1
by Aaron Bentley
Fake merge of pre-versioned-ids-for-charms. |
617 |
self.makeCharm(name='popular2', downloads=1) |
618 |
self.makeCharm(name='popular3', downloads=2) |
|
619 |
order132 = ['popular1', 'popular3', 'popular2'] |
|
197.1.4
by Aaron Bentley
Implement searching by popularity. |
620 |
result = self.index_client.api_search() |
362.1.8
by Curtis Hovey
Remove extra lines. |
621 |
self.assertNotEqual(order132, [r['data']['name'] for r in result]) |
197.1.4
by Aaron Bentley
Implement searching by popularity. |
622 |
result = self.index_client.api_search(sort='downloaded') |
362.1.8
by Curtis Hovey
Remove extra lines. |
623 |
self.assertEqual(order132, [r['data']['name'] for r in result]) |
197.1.6
by Aaron Bentley
Sort by date_created works. |
624 |
|
625 |
def test_search_charm_sort_newest(self): |
|
626 |
"""Specifying 'new' sorts results appropriately."""
|
|
627 |
self.makeCharm(name='new1', date_created=datetime(2013, 3, 1)) |
|
628 |
self.makeCharm(name='new2', date_created=datetime(2013, 1, 2)) |
|
629 |
self.makeCharm(name='new3', date_created=datetime(2013, 1, 1)) |
|
630 |
order123 = ['new1', 'new2', 'new3'] |
|
631 |
result = self.index_client.api_search() |
|
362.1.8
by Curtis Hovey
Remove extra lines. |
632 |
self.assertNotEqual(order123, [r['data']['name'] for r in result]) |
197.1.6
by Aaron Bentley
Sort by date_created works. |
633 |
result = self.index_client.api_search(sort='new') |
362.1.8
by Curtis Hovey
Remove extra lines. |
634 |
self.assertEqual(order123, [r['data']['name'] for r in result]) |
197.1.8
by Aaron Bentley
Exclude unknown sorts. |
635 |
|
636 |
def test_search_unknown_sort(self): |
|
637 |
"""Specifying an unknown sort raises an exception."""
|
|
638 |
with self.assertRaises(ValueError) as e: |
|
197.1.9
by Aaron Bentley
Cleanup. |
639 |
self.index_client.api_search(sort='foo') |
197.1.8
by Aaron Bentley
Exclude unknown sorts. |
640 |
self.assertEqual(str(e.exception), 'Invalid sort: foo.') |
221.2.1
by Aaron Bentley
Add ability to manipulate aliases. |
641 |
|
642 |
def test_update_alias(self): |
|
643 |
alias = ElasticSearchClient(self.index_client._client, |
|
644 |
'temp_index_alias') |
|
645 |
self.assertEqual([], alias.get_aliased()) |
|
646 |
alias.update_aliased(self.index_client.index_name, []) |
|
647 |
self.assertEqual([self.index_client.index_name], alias.get_aliased()) |
|
648 |
self.use_context(temp_index_client('foo')) |
|
649 |
alias.update_aliased('foo', []) |
|
650 |
self.assertItemsEqual([self.index_client.index_name, 'foo'], |
|
651 |
alias.get_aliased()) |
|
652 |
self.use_context(temp_index_client('bar')) |
|
653 |
alias.update_aliased('bar', alias.get_aliased()) |
|
654 |
self.assertEqual(['bar'], alias.get_aliased()) |
|
221.2.2
by Aaron Bentley
Add ability to copy indices. |
655 |
|
344.2.2
by Brad Crittenden
Changed test names for clarity as suggested in review. |
656 |
def test_create_replacement_charm_indexed_in_place(self): |
302.4.10
by Aaron Bentley
In index, put charm document into data attribute, to support ID separation. |
657 |
charm_data = {'_id': 'a', 'name': 'foo', 'owner': 'bar', |
658 |
'series': 'baz'} |
|
659 |
self.index_client.index_charm(charm_data) |
|
241.1.4
by Aaron Bentley
reindex permits specifying charms. |
660 |
copy = self.index_client.create_replacement('index-copy') |
221.2.2
by Aaron Bentley
Add ability to copy indices. |
661 |
self.addCleanup(copy.delete_index) |
662 |
copy.wait_for_startup() |
|
302.5.1
by Aaron Bentley
Fake merge of pre-versioned-ids-for-charms. |
663 |
mapping = copy.get_mapping()[CHARM] |
664 |
self.assertIn('series', mapping['properties']['data']['properties']) |
|
302.4.10
by Aaron Bentley
In index, put charm document into data attribute, to support ID separation. |
665 |
self.assertEqual(charm_data, copy.get('a')) |
221.2.3
by Aaron Bentley
Ensure unique failure for incompatible mapping. |
666 |
|
344.2.2
by Brad Crittenden
Changed test names for clarity as suggested in review. |
667 |
def test_create_replacement_bundle_indexed_in_place(self): |
344.2.1
by Brad Crittenden
Make create_replacement be bundle aware. |
668 |
bundle_data = {'_id': 'a', 'name': 'foo', 'owner': 'bar', |
358.1.10
by Brad Crittenden
Fix tests from changing bundle revision use. |
669 |
'basket_name': 'basket', 'data': {'series': 'baz'}} |
344.2.1
by Brad Crittenden
Make create_replacement be bundle aware. |
670 |
self.index_client.index_bundle(bundle_data) |
671 |
copy = self.index_client.create_replacement('index-copy') |
|
672 |
self.addCleanup(copy.delete_index) |
|
673 |
copy.wait_for_startup() |
|
351.1.2
by Benji York
finish up the branch |
674 |
mapping = copy.get_mapping(doctype=BUNDLE)[BUNDLE] |
675 |
self.assertIn('series', mapping['properties']['data']['properties']) |
|
676 |
self.assertEqual( |
|
677 |
{u'_id': u'~bar/basket/foo', u'data': bundle_data}, |
|
678 |
copy.get(Bundle(bundle_data).id, BUNDLE)) |
|
344.2.1
by Brad Crittenden
Make create_replacement be bundle aware. |
679 |
|
344.2.2
by Brad Crittenden
Changed test names for clarity as suggested in review. |
680 |
def test_create_replacement_charms_replacing_old(self): |
302.4.10
by Aaron Bentley
In index, put charm document into data attribute, to support ID separation. |
681 |
charm_data = {'_id': 'a', 'name': 'foo', 'owner': 'bar', |
682 |
'series': 'baz'} |
|
683 |
charm_data2 = {'_id': 'a', 'name': 'bar', 'owner': 'bar', |
|
684 |
'series': 'baz'} |
|
685 |
self.index_client.index_charm(charm_data) |
|
241.1.4
by Aaron Bentley
reindex permits specifying charms. |
686 |
copy = self.index_client.create_replacement( |
302.4.10
by Aaron Bentley
In index, put charm document into data attribute, to support ID separation. |
687 |
charms=[charm_data2]) |
241.1.4
by Aaron Bentley
reindex permits specifying charms. |
688 |
self.addCleanup(copy.delete_index) |
689 |
copy.wait_for_startup() |
|
302.5.1
by Aaron Bentley
Fake merge of pre-versioned-ids-for-charms. |
690 |
mapping = copy.get_mapping()[CHARM] |
691 |
self.assertIn('series', mapping['properties']['data']['properties']) |
|
302.4.10
by Aaron Bentley
In index, put charm document into data attribute, to support ID separation. |
692 |
self.assertEqual(charm_data2, copy.get('a')) |
241.1.4
by Aaron Bentley
reindex permits specifying charms. |
693 |
|
344.2.2
by Brad Crittenden
Changed test names for clarity as suggested in review. |
694 |
def test_create_replacement_bundles_replacing_old(self): |
344.2.1
by Brad Crittenden
Make create_replacement be bundle aware. |
695 |
bundle_data = {'_id': 'a', 'name': 'foo', 'owner': 'bar', |
358.1.10
by Brad Crittenden
Fix tests from changing bundle revision use. |
696 |
'series': 'first', 'basket_name': 'basket'} |
351.1.2
by Benji York
finish up the branch |
697 |
bundle_data2 = {'_id': 'a', 'name': 'foo', 'owner': 'bar', |
358.1.10
by Brad Crittenden
Fix tests from changing bundle revision use. |
698 |
'series': 'second', 'basket_name': 'basket'} |
344.2.1
by Brad Crittenden
Make create_replacement be bundle aware. |
699 |
self.index_client.index_bundle(bundle_data) |
700 |
copy = self.index_client.create_replacement( |
|
701 |
bundles=[bundle_data2]) |
|
702 |
self.addCleanup(copy.delete_index) |
|
703 |
copy.wait_for_startup() |
|
351.1.2
by Benji York
finish up the branch |
704 |
mapping = copy.get_mapping(doctype=BUNDLE)[BUNDLE] |
705 |
self.assertIn('series', mapping['properties']['data']['properties']) |
|
706 |
self.assertEqual( |
|
707 |
{u'_id': u'~bar/basket/foo', u'data': bundle_data2}, |
|
708 |
copy.get(Bundle(bundle_data).id, BUNDLE)) |
|
709 |
||
344.2.1
by Brad Crittenden
Make create_replacement be bundle aware. |
710 |
def test_create_replacement_missing(self): |
241.1.5
by Aaron Bentley
Handle reindexing when the index does not exist. |
711 |
client = ElasticSearchClient.from_settings(get_ini(), 'temp-index') |
241.1.9
by Aaron Bentley
Comment tests that prove no exception raised. |
712 |
# This should not raise an exception, even though the index has not
|
713 |
# been created.
|
|
241.1.5
by Aaron Bentley
Handle reindexing when the index does not exist. |
714 |
copy = client.create_replacement() |
715 |
self.addCleanup(copy.delete_index) |
|
716 |
||
221.2.3
by Aaron Bentley
Ensure unique failure for incompatible mapping. |
717 |
def test_put_mapping_incompatible_mapping_error(self): |
718 |
# The error we get from an incompatible mapping is IncompatibleMapping
|
|
719 |
context = temp_index_client(name='foo', put_mapping=False) |
|
720 |
client = self.use_context(context) |
|
302.4.10
by Aaron Bentley
In index, put charm document into data attribute, to support ID separation. |
721 |
put_incompatible_mapping(client) |
221.2.3
by Aaron Bentley
Ensure unique failure for incompatible mapping. |
722 |
with self.assertRaises(IncompatibleMapping): |
723 |
client.put_mapping() |
|
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
724 |
|
221.3.3
by Aaron Bentley
put_mapping raises IndexMissing as needed. |
725 |
def test_put_mapping_missing_index(self): |
726 |
# The error we get from a missing index is IndexMissing
|
|
727 |
client = ElasticSearchClient.from_settings(get_ini(), 'temp-index') |
|
728 |
with self.assertRaises(IndexMissing): |
|
729 |
client.put_mapping() |
|
221.2.13
by Aaron Bentley
Merged migrate-elasticsearch into migrate. |
730 |
|
324.1.2
by Brad Crittenden
Add support for bundles in search |
731 |
def test_get_mapping_default(self): |
332.1.1
by Brad Crittenden
Change api_search and related charms to ignore bundles for now. |
732 |
# Calling get_mapping with no doctype returns charms.
|
324.1.2
by Brad Crittenden
Add support for bundles in search |
733 |
client = self.index_client |
734 |
client.put_mapping() |
|
735 |
mapping = client.get_mapping() |
|
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
736 |
self.assertEqual([CHARM], mapping.keys()) |
324.1.2
by Brad Crittenden
Add support for bundles in search |
737 |
|
738 |
def test_get_mapping_charms(self): |
|
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
739 |
# Calling get_mapping with CHARM returns charms.
|
324.1.2
by Brad Crittenden
Add support for bundles in search |
740 |
client = self.index_client |
741 |
client.put_mapping() |
|
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
742 |
mapping = client.get_mapping(CHARM) |
743 |
self.assertEqual([CHARM], mapping.keys()) |
|
324.1.2
by Brad Crittenden
Add support for bundles in search |
744 |
|
745 |
def test_get_mapping_bundles(self): |
|
746 |
# Calling get_mapping with 'bundles' returns bundles.
|
|
747 |
client = self.index_client |
|
748 |
client.put_mapping() |
|
749 |
mapping = client.get_mapping('bundle') |
|
750 |
self.assertEqual(['bundle'], mapping.keys()) |
|
751 |
||
752 |
def test_get_mapping_all(self): |
|
753 |
# Calling get_mapping with None returns full mapping.
|
|
754 |
client = self.index_client |
|
755 |
client.put_mapping() |
|
756 |
mapping = client.get_mapping(None) |
|
757 |
self.assertEqual(['temp_index'], mapping.keys()) |
|
758 |
self.assertEqual( |
|
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
759 |
['bundle', CHARM], |
324.1.2
by Brad Crittenden
Add support for bundles in search |
760 |
sorted(mapping['temp_index'].keys())) |
761 |
||
367.2.16
by Curtis Hovey
Added tests to verify get_items() returns charms and bundles data. |
762 |
def test_get_items(self): |
763 |
# The method returns the typed documents that match the typed_ids.
|
|
764 |
charm_data = {'name': 'charm', 'owner': 'owner1', 'series': 'series'} |
|
765 |
self.index_client.index_charm(charm_data) |
|
766 |
bundle_data = { |
|
767 |
'name': 'bundle', 'owner': 'owner2', 'basket_name': 'basket'} |
|
768 |
self.index_client.index_bundle(bundle_data) |
|
769 |
typed_ids = [ |
|
770 |
{'_id': '~owner1/series/charm', '_type': CHARM}, |
|
771 |
{'_id': '~owner2/basket/bundle', '_type': BUNDLE} |
|
772 |
]
|
|
773 |
docs = self.index_client.get_items(typed_ids) |
|
774 |
expected = [ |
|
775 |
{'data': charm_data, 'doctype': CHARM}, |
|
776 |
{'data': bundle_data, 'doctype': BUNDLE} |
|
777 |
]
|
|
778 |
self.assertEqual(expected, docs) |
|
779 |
||
367.2.15
by Curtis Hovey
Added a test to verify that passing an empty list to get_items() retuns an empty list. |
780 |
def test_get_items_with_empty_list(self): |
367.2.16
by Curtis Hovey
Added tests to verify get_items() returns charms and bundles data. |
781 |
# When test_ids is an empty list, the list of docs is also empty.
|
367.2.15
by Curtis Hovey
Added a test to verify that passing an empty list to get_items() retuns an empty list. |
782 |
self.assertEqual([], self.index_client.get_items([])) |
783 |
||
217.1.9
by Aaron Bentley
Implement related-interface search. |
784 |
def test_related_charms(self): |
785 |
client = self.index_client |
|
786 |
charm = factory.get_charm_json(provides={'abc': {'interface': 'def'}}) |
|
787 |
client.index_charm(charm) |
|
217.1.17
by Aaron Bentley
Fix test failure due to signature change. |
788 |
requires, provides = client.related_charms( |
789 |
set(charm['i_provides']), set(charm['i_requires'])) |
|
217.1.9
by Aaron Bentley
Implement related-interface search. |
790 |
self.assertEqual({}, provides) |
791 |
self.assertEqual({}, requires) |
|
217.1.17
by Aaron Bentley
Fix test failure due to signature change. |
792 |
requires, provides = client.related_charms(set(['def']), set(['def'])) |
217.1.18
by Aaron Bentley
Update related_charms to include weight. |
793 |
self.assertEqual(['def'], provides.keys()) |
217.1.23
by Aaron Bentley
Change related_charms() to match search() |
794 |
self.assertEqual(charm, provides['def'][0]['data']) |
795 |
self.assertIs(float, type(provides['def'][0]['weight'])) |
|
217.1.18
by Aaron Bentley
Update related_charms to include weight. |
796 |
self.assertEqual(2, len(provides['def'][0])) |
217.1.9
by Aaron Bentley
Implement related-interface search. |
797 |
self.assertEqual({}, requires) |
798 |
charm2 = factory.get_charm_json( |
|
217.1.21
by Aaron Bentley
Fix lint. |
799 |
provides={ |
800 |
'abc': {'interface': 'def'}, |
|
801 |
'mno': {'interface': 'pqr'}}, |
|
217.1.9
by Aaron Bentley
Implement related-interface search. |
802 |
requires={'ghi': {'interface': 'jkl'}}) |
803 |
client.index_charm(charm2) |
|
217.1.17
by Aaron Bentley
Fix test failure due to signature change. |
804 |
requires, provides = client.related_charms(set(['jkl']), set()) |
217.1.9
by Aaron Bentley
Implement related-interface search. |
805 |
self.assertEqual({}, provides) |
217.1.18
by Aaron Bentley
Update related_charms to include weight. |
806 |
self.assertEqual(['jkl'], requires.keys()) |
217.1.23
by Aaron Bentley
Change related_charms() to match search() |
807 |
self.assertEqual(charm2, requires['jkl'][0]['data']) |
217.1.17
by Aaron Bentley
Fix test failure due to signature change. |
808 |
requires, provides = client.related_charms(set(['jkl']), set(['def'])) |
217.1.9
by Aaron Bentley
Implement related-interface search. |
809 |
self.assertEqual(['def'], provides.keys()) |
217.1.18
by Aaron Bentley
Update related_charms to include weight. |
810 |
self.assertItemsEqual([charm, charm2], |
217.1.23
by Aaron Bentley
Change related_charms() to match search() |
811 |
[payload['data'] for payload in provides['def']]) |
217.1.18
by Aaron Bentley
Update related_charms to include weight. |
812 |
self.assertEqual([charm2], |
217.1.23
by Aaron Bentley
Change related_charms() to match search() |
813 |
[payload['data'] for payload in requires['jkl']]) |
217.1.17
by Aaron Bentley
Fix test failure due to signature change. |
814 |
requires, provides = client.related_charms(set(), |
815 |
set(['def', 'pqr', 'xyz'])) |
|
217.1.9
by Aaron Bentley
Implement related-interface search. |
816 |
self.assertItemsEqual(['def', 'pqr'], provides.keys()) |
217.1.23
by Aaron Bentley
Change related_charms() to match search() |
817 |
self.assertEqual([charm2], [payload['data'] |
818 |
for payload in provides['pqr']]) |
|
217.1.18
by Aaron Bentley
Update related_charms to include weight. |
819 |
self.assertItemsEqual([charm, charm2], |
217.1.23
by Aaron Bentley
Change related_charms() to match search() |
820 |
[payload['data'] for payload in provides['def']]) |
217.1.9
by Aaron Bentley
Implement related-interface search. |
821 |
|
217.1.22
by Aaron Bentley
Boosting applies to related_charms() |
822 |
def test_related_charms_official_boost(self): |
823 |
"""An official version of a charm rates 10x higher."""
|
|
824 |
charm = factory.get_charm_json( |
|
225.2.10
by Abel Deuring
trunk merged; conflicts and new test failures fixed. |
825 |
provides={'foo': {'interface': 'telnet'}}, promulgated=True) |
217.1.22
by Aaron Bentley
Boosting applies to related_charms() |
826 |
official_id = charm['_id'] |
827 |
unofficial_id = official_id + '-1' |
|
828 |
client = self.index_client |
|
829 |
client.index_charm(charm) |
|
830 |
charm['owner'] = 'jrandom' |
|
831 |
charm['_id'] = unofficial_id |
|
225.2.18
by Abel Deuring
ElasticSearch queries: Do not assuma that charmers are the only owners of promulgated charms. |
832 |
charm['store_url'] = get_address(charm, short=False) |
217.1.22
by Aaron Bentley
Boosting applies to related_charms() |
833 |
client.index_charm(charm) |
834 |
requires, provides = client.related_charms(set(), set(['telnet'])) |
|
217.1.23
by Aaron Bentley
Change related_charms() to match search() |
835 |
by_id = dict((result['data']['_id'], result['weight']) |
217.1.22
by Aaron Bentley
Boosting applies to related_charms() |
836 |
for result in provides['telnet']) |
837 |
self.assertAlmostEqual(by_id[official_id], by_id[unofficial_id] * 10) |
|
838 |
||
240.5.16
by Aaron Bentley
Related charms is limited to the same series. |
839 |
def test_related_charms_series_filter(self): |
840 |
def filter_data(result): |
|
841 |
return dict((key, [entry['data'] for entry in value]) |
|
842 |
for key, value in result.items()) |
|
843 |
alpha_charm = factory.get_charm_json( |
|
844 |
series='alpha', provides={'asdf': {'interface': 'foo'}}) |
|
845 |
beta_charm = factory.get_charm_json( |
|
846 |
series='beta', requires={'asdf': {'interface': 'bar'}}) |
|
847 |
self.index_client.index_charms([alpha_charm, beta_charm]) |
|
848 |
requires, provides = self.index_client.related_charms({'bar'}, {'foo'}) |
|
849 |
self.assertEqual({'bar': [beta_charm]}, filter_data(requires)) |
|
850 |
self.assertEqual({'foo': [alpha_charm]}, filter_data(provides)) |
|
851 |
requires, provides = self.index_client.related_charms( |
|
852 |
{'bar'}, {'foo'}, series='alpha') |
|
853 |
self.assertEqual({}, filter_data(requires)) |
|
854 |
self.assertEqual({'foo': [alpha_charm]}, filter_data(provides)) |
|
855 |
requires, provides = self.index_client.related_charms( |
|
856 |
{'bar'}, {'foo'}, series='beta') |
|
857 |
self.assertEqual({'bar': [beta_charm]}, filter_data(requires)) |
|
858 |
self.assertEqual({}, filter_data(provides)) |
|
859 |
requires, provides = self.index_client.related_charms( |
|
860 |
{'bar'}, {'foo'}, series='gamma') |
|
861 |
self.assertEqual({}, filter_data(requires)) |
|
862 |
self.assertEqual({}, filter_data(provides)) |
|
863 |
||
251.1.1
by Aaron Bentley
Support exclude_name in ESC.related_charms. |
864 |
def test_related_charms_exclude_name(self): |
865 |
charm = factory.get_charm_json( |
|
866 |
requires={'asdf': {'interface': 'bar'}}) |
|
867 |
self.index_client.index_charm(charm) |
|
868 |
requires, provides = self.index_client.related_charms({'bar'}, set()) |
|
869 |
self.assertEqual(charm, requires['bar'][0]['data']) |
|
870 |
requires, provides = self.index_client.related_charms( |
|
871 |
{'bar'}, set(), exclude_name=charm['name']) |
|
872 |
self.assertEqual({}, requires) |
|
873 |
||
251.2.1
by Abel Deuring
sanitize full text search strings to avoid parsing errors |
874 |
def test_special_characters_in_full_text_term(self): |
875 |
# Some characters have a special meaning for the text parser
|
|
876 |
# used by ElasticSearch/Lucene. Since these characters can
|
|
877 |
# cause exceptions when used unexpectedly, like unbalanced
|
|
878 |
# parentheses, they are replaced with spaces.
|
|
879 |
for char in range(0, 128): |
|
880 |
char = chr(char) |
|
881 |
try: |
|
882 |
text = u'foo %s bar' % char |
|
883 |
self.index_client.search(text) |
|
884 |
except ElasticHttpError: |
|
885 |
self.fail( |
|
886 |
"Character '%s' inside a search term causes " |
|
887 |
"ElasticHttpError" % char) |
|
888 |
try: |
|
889 |
text = 'foo%s' % char |
|
890 |
self.index_client.search(text) |
|
891 |
except ElasticHttpError: |
|
892 |
self.fail( |
|
893 |
"Character '%s' at the end of a search term causes " |
|
894 |
"ElasticHttpError" % char) |
|
895 |
||
370
by Abel Deuring
tests with spurious failures re-enabled; spurious IndexMissing exceptions in temp_index_client() and in test_exodus_update_runs_against_temp_but_generates_pending() fixed. |
896 |
def test_mapping_changes_during_indexing(self): |
346.1.1
by Abel Deuring
Define a static mapping for charms. Note: The properties 'provides' and 'requires' are still dynamic. |
897 |
# When a charm is indexed, only the mappings for the properties
|
898 |
# "requires" and "provides" change.
|
|
346.1.3
by Abel Deuring
test failures fixed. |
899 |
before = self.index_client.get_mapping()['charm']['properties'] |
900 |
before = before['data']['properties'] |
|
346.1.1
by Abel Deuring
Define a static mapping for charms. Note: The properties 'provides' and 'requires' are still dynamic. |
901 |
charm = factory.get_charm_json() |
902 |
self.index_client.index_charm(charm) |
|
346.1.3
by Abel Deuring
test failures fixed. |
903 |
after = self.index_client.get_mapping()['charm']['properties'] |
904 |
after = after['data']['properties'] |
|
905 |
before_provides = before.pop('provides') |
|
906 |
before_requires = before.pop('requires') |
|
907 |
after_provides = after.pop('provides') |
|
908 |
after_requires = after.pop('requires') |
|
346.1.5
by Abel Deuring
migration for the new mapping added. |
909 |
self.assertEqual(before, after) |
346.1.1
by Abel Deuring
Define a static mapping for charms. Note: The properties 'provides' and 'requires' are still dynamic. |
910 |
self.assertNotEqual(before_provides, after_provides) |
911 |
self.assertNotEqual(before_requires, after_requires) |
|
912 |
||
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
913 |
|
320.1.1
by Benji York
add indexing of bundles |
914 |
class TestIndexingBundles(TestCase): |
915 |
||
916 |
def setUp(self): |
|
917 |
super(TestIndexingBundles, self).setUp() |
|
918 |
self.use_index_client() |
|
919 |
||
920 |
def exists_in_index(self, bundle_id): |
|
921 |
try: |
|
922 |
result = self.index_client._client.get( |
|
923 |
self.index_client.index_name, 'bundle', bundle_id) |
|
924 |
except ElasticHttpNotFoundError: |
|
925 |
return False |
|
926 |
return result['exists'] |
|
927 |
||
928 |
def test_index_bundles(self): |
|
929 |
bundle_data = factory.get_bundle_data() |
|
351.1.2
by Benji York
finish up the branch |
930 |
search_id = Bundle(bundle_data).search_id |
931 |
self.assertFalse(self.exists_in_index(search_id)) |
|
320.1.1
by Benji York
add indexing of bundles |
932 |
self.index_client.index_bundles([bundle_data]) |
351.1.2
by Benji York
finish up the branch |
933 |
self.assertTrue(self.exists_in_index(search_id)) |
320.1.1
by Benji York
add indexing of bundles |
934 |
|
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
935 |
def test_index_bundles_no_bundles(self): |
936 |
# Assert that no exception is raised.
|
|
937 |
with self.assertRaises(AssertionError): |
|
938 |
with self.assertRaises(BaseException): |
|
939 |
self.index_client.index_bundles([]) |
|
940 |
||
351.1.1
by Aaron Bentley
Refactor and implement versionless bundle ids. |
941 |
def test_only_one_revision_per_bundle(self): |
942 |
bundle_data = factory.get_bundle_data( |
|
358.1.14
by Brad Crittenden
Sort in mongo. Better name basket_with_rev for get_bundle_data. |
943 |
owner='a', name='b', basket_with_rev='c/1') |
351.1.1
by Aaron Bentley
Refactor and implement versionless bundle ids. |
944 |
bundle_data_2 = factory.get_bundle_data( |
358.1.14
by Brad Crittenden
Sort in mongo. Better name basket_with_rev for get_bundle_data. |
945 |
owner='a', name='b', basket_with_rev='c/2') |
351.1.1
by Aaron Bentley
Refactor and implement versionless bundle ids. |
946 |
self.index_client.index_bundle(bundle_data) |
947 |
self.index_client.index_bundle(bundle_data_2) |
|
362.1.2
by Curtis Hovey
Always include the doctype with api_search results when the doctype is not specified. |
948 |
self.assertEqual(['~a/c/2/b'], [b['data']['_id'] for b in |
351.1.1
by Aaron Bentley
Refactor and implement versionless bundle ids. |
949 |
self.index_client.api_search(doctype=None)]) |
950 |
||
320.1.1
by Benji York
add indexing of bundles |
951 |
|
346.1.5
by Abel Deuring
migration for the new mapping added. |
952 |
def put_mapping(client, properties, dynamic=True): |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
953 |
client._client.put_mapping( |
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
954 |
client.index_name, CHARM, { |
955 |
CHARM: { |
|
346.1.5
by Abel Deuring
migration for the new mapping added. |
956 |
'dynamic': dynamic, |
302.5.1
by Aaron Bentley
Fake merge of pre-versioned-ids-for-charms. |
957 |
'properties': {'data': {'properties': properties}} |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
958 |
}
|
959 |
}
|
|
960 |
)
|
|
961 |
||
962 |
||
302.4.10
by Aaron Bentley
In index, put charm document into data attribute, to support ID separation. |
963 |
def put_incompatible_mapping(index_client): |
964 |
put_mapping(index_client, { |
|
965 |
'name': {'type': 'string', 'index': 'analyzed'}, |
|
966 |
})
|
|
967 |
||
968 |
||
221.2.12
by Aaron Bentley
Rename migrate to update. |
969 |
class TestUpdate(TestCase): |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
970 |
|
221.2.12
by Aaron Bentley
Rename migrate to update. |
971 |
def test_update_noop(self): |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
972 |
temp_client = self.use_index_client() |
221.2.12
by Aaron Bentley
Rename migrate to update. |
973 |
update(temp_client) |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
974 |
|
221.2.12
by Aaron Bentley
Rename migrate to update. |
975 |
def test_compatible_update(self): |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
976 |
index_client = self.use_index_client(put_mapping=False) |
977 |
put_mapping(index_client, { |
|
324.1.1
by Brad Crittenden
Make generic search routines for future support of charms and bundles. |
978 |
'box': {'type': 'string', 'index': 'not_analyzed'}, |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
979 |
})
|
324.1.3
by Brad Crittenden
Add tests for bundle searching methods |
980 |
mapping = index_client.get_mapping()[CHARM] |
302.5.1
by Aaron Bentley
Fake merge of pre-versioned-ids-for-charms. |
981 |
self.assertNotIn('name', mapping['properties']['data']['properties']) |
221.2.12
by Aaron Bentley
Rename migrate to update. |
982 |
update(index_client) |
302.5.1
by Aaron Bentley
Fake merge of pre-versioned-ids-for-charms. |
983 |
mapping = index_client.get_mapping()[CHARM]['properties']['data'] |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
984 |
self.assertIn('box', mapping['properties']) |
985 |
self.assertIn('name', mapping['properties']) |
|
986 |
||
221.2.12
by Aaron Bentley
Rename migrate to update. |
987 |
def test_incompatible_update(self): |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
988 |
index_client = self.use_index_client(put_mapping=False) |
302.4.10
by Aaron Bentley
In index, put charm document into data attribute, to support ID separation. |
989 |
put_incompatible_mapping(index_client) |
221.2.12
by Aaron Bentley
Rename migrate to update. |
990 |
update(index_client) |
302.5.1
by Aaron Bentley
Fake merge of pre-versioned-ids-for-charms. |
991 |
mapping = index_client.get_mapping()[CHARM]['properties']['data'] |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
992 |
self.assertEqual('not_analyzed', |
993 |
mapping['properties']['name']['index']) |
|
994 |
||
221.2.14
by Aaron Bentley
Handle no existing index, simplify ensure_index to create_index. |
995 |
def test_update_no_index(self): |
996 |
index_client = ElasticSearchClient.from_settings( |
|
997 |
get_ini(), 'temp-index') |
|
998 |
actual_client = update(index_client) |
|
999 |
self.addCleanup(actual_client.delete_index) |
|
1000 |
self.assertEqual([actual_client.index_name], |
|
1001 |
index_client.get_aliased()) |
|
302.5.1
by Aaron Bentley
Fake merge of pre-versioned-ids-for-charms. |
1002 |
mapping = index_client.get_mapping()[CHARM]['properties']['data'] |
221.2.14
by Aaron Bentley
Handle no existing index, simplify ensure_index to create_index. |
1003 |
self.assertEqual('not_analyzed', |
1004 |
mapping['properties']['name']['index']) |
|
1005 |
||
346.1.5
by Abel Deuring
migration for the new mapping added. |
1006 |
def update_to_static_mapping(self, force_reindex): |
1007 |
index_client = self.use_index_client(put_mapping=False) |
|
1008 |
put_mapping( |
|
1009 |
index_client, |
|
1010 |
{'box': {'type': 'string', 'index': 'not_analyzed'}}, |
|
1011 |
dynamic=True) |
|
375
by Abel Deuring
fix spurious test failure in test_dynamic_to_static_mapping_forced_reindex() |
1012 |
# Prevent spurious exceptions
|
1013 |
# "InvalidIndexNameException[[temp_index] Invalid index name
|
|
1014 |
# [temp_index], an alias with the same name already exists"
|
|
346.1.5
by Abel Deuring
migration for the new mapping added. |
1015 |
update(index_client, force_reindex) |
376
by Abel Deuring
use the condition 'wait_for_status=green' in ElasticSearchClient.wait_for_startup(); use wait_for_startup() instead of client._health(...) |
1016 |
index_client.wait_for_startup() |
346.1.5
by Abel Deuring
migration for the new mapping added. |
1017 |
updated_mapping = index_client.get_mapping() |
1018 |
# A property 'files' is not defined in the current mapping.
|
|
1019 |
self.assertNotIn( |
|
1020 |
'files', |
|
1021 |
updated_mapping['charm']['properties']['data']['properties']) |
|
1022 |
index_client.index_charm(factory.get_charm_json()) |
|
1023 |
return index_client |
|
1024 |
||
375
by Abel Deuring
fix spurious test failure in test_dynamic_to_static_mapping_forced_reindex() |
1025 |
def test_simple_change_dynamic_to_static_mapping(self): |
346.1.5
by Abel Deuring
migration for the new mapping added. |
1026 |
# If an existing mapping is dynamic (the default for ElasticSearch)
|
1027 |
# and if a new mapping is specified as static, the two mappings
|
|
1028 |
# are considered compatible, but the resulting mapping is
|
|
1029 |
# still dynamic.
|
|
1030 |
index_client = self.update_to_static_mapping(force_reindex=False) |
|
1031 |
updated_mapping = index_client.get_mapping() |
|
1032 |
# charm['files'] is not supposed to be included in the new
|
|
1033 |
# mapping, but it still exists if force_reindex is not used.
|
|
1034 |
self.assertIn( |
|
1035 |
'files', |
|
1036 |
updated_mapping['charm']['properties']['data']['properties']) |
|
1037 |
||
1038 |
def test_dynamic_to_static_mapping_forced_reindex(self): |
|
1039 |
# If an existing mapping is dynamic (the defult) and if a
|
|
1040 |
# new mapping is specified as static, the two mappings
|
|
1041 |
# are considered compatible, but the resulting mapping is
|
|
1042 |
# still dynamic.
|
|
1043 |
index_client = self.update_to_static_mapping(force_reindex=True) |
|
1044 |
updated_mapping = index_client.get_mapping() |
|
1045 |
# The mapping is indeed static; charm['files'] is not indexed.
|
|
1046 |
self.assertNotIn( |
|
1047 |
'files', |
|
1048 |
updated_mapping['charm']['properties']['data']['properties']) |
|
1049 |
||
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
1050 |
|
1051 |
class TestReindex(TestCase): |
|
1052 |
||
1053 |
def test_reindex_aliased(self): |
|
1054 |
index_client = ElasticSearchClient.from_settings( |
|
1055 |
get_ini(), 'temp_aliased') |
|
1056 |
alias = ElasticSearchClient(index_client._client, 'alias') |
|
221.2.14
by Aaron Bentley
Handle no existing index, simplify ensure_index to create_index. |
1057 |
index_client.create_index() |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
1058 |
try: |
1059 |
alias.update_aliased(index_client.index_name, []) |
|
1060 |
except: |
|
1061 |
index_client.delete_index() |
|
1062 |
raise
|
|
1063 |
self.addCleanup(alias.delete_index) |
|
302.4.10
by Aaron Bentley
In index, put charm document into data attribute, to support ID separation. |
1064 |
put_incompatible_mapping(index_client) |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
1065 |
self.assertEqual(['temp_aliased'], alias.get_aliased()) |
226.1.1
by Aaron Bentley
Fix transient test failure. |
1066 |
index_client.wait_for_startup() |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
1067 |
new_aliased = reindex(alias) |
1068 |
self.assertEqual([new_aliased.index_name], alias.get_aliased()) |
|
302.5.1
by Aaron Bentley
Fake merge of pre-versioned-ids-for-charms. |
1069 |
mapping = alias.get_mapping()[CHARM]['properties']['data'] |
221.2.5
by Aaron Bentley
Implement reindex and migrate. |
1070 |
self.assertEqual('not_analyzed', |
302.5.1
by Aaron Bentley
Fake merge of pre-versioned-ids-for-charms. |
1071 |
mapping['properties']['name']['index']) |
241.1.5
by Aaron Bentley
Handle reindexing when the index does not exist. |
1072 |
|
1073 |
def test_reindexed_no_client_charms(self): |
|
1074 |
client = ElasticSearchClient.from_settings(get_ini()) |
|
241.1.9
by Aaron Bentley
Comment tests that prove no exception raised. |
1075 |
# This should not raise an exception, even though the index does not
|
1076 |
# exist.
|
|
241.1.5
by Aaron Bentley
Handle reindexing when the index does not exist. |
1077 |
new_client = reindex(client, charms=[]) |
1078 |
new_client.delete_index() |