167
171
def related_weightless(cls, charm):
168
172
return cls.weightless(cls.api_class._format_related(charm, 0))
174
def make_indexed_bundle(self, *args, **kwargs):
175
bundle, ignore = factory.makeBundle(self.db, *args, **kwargs)
176
bundle._representation['_id'] = bundle.id
177
self.index_client.index_bundle(bundle._representation)
180
def make_indexed_charm(self, *args, **kwargs):
181
ignore, charm = factory.makeCharm(self.db, *args, **kwargs)
182
self.index_client.index_charm(charm)
171
186
class API3Mixin(APITestBase):
189
search_endpoint = 'search'
176
192
class API2Mixin(APITestBase):
195
search_endpoint = 'charms'
181
198
class TestAPICharms:
195
212
def test_remote_friendly_headers(self):
196
213
"""The Juju-Gui needs to be able to query the api via diff origin."""
197
214
self.index_client.wait_for_startup()
198
resp = self.get_response('charms')
215
resp = self.get_response(self.search_endpoint)
199
216
self.assertIn(("Access-Control-Allow-Origin", "*"), resp.headerlist)
201
218
("Access-Control-Allow-Headers", "X-Requested-With"),
204
221
def get_charms(self, *args, **kwargs):
205
return self.get_response('charms', *args, **kwargs).json_body['result']
222
return self.get_response(
223
self.search_endpoint, *args, **kwargs).json_body['result']
207
225
def _test_filter(self, filter_str, make_charm=None):
208
226
if make_charm is None:
278
296
self.assertEqual([charmers], [charm for charm in result])
279
297
result = self.get_charms(type_='environment')
280
298
self.assertEqual([], [charm['owner'] for charm in result])
281
response = self.get_response('charms', type_='foo')
299
response = self.get_response(
300
self.search_endpoint, type_='foo')
282
301
self.assertEqual(406, response.status_code)
283
302
self.assertEqual({'type': 'unsupported_value', 'parameter': 'type',
284
303
'value': 'foo'}, response.json_body)
301
320
def test_charms_accepts_provider(self):
302
321
"""Provider is accepted (but ignored)."""
303
response = self.get_response('charms', provider='asdf')
322
response = self.get_response(self.search_endpoint, provider='asdf')
304
323
self.assertEqual(200, response.status_code)
306
325
def test_charms_accepts_scope(self):
307
326
"""Scope is accepted (but ignored)."""
308
response = self.get_response('charms', scope='asdf')
327
response = self.get_response(self.search_endpoint, scope='asdf')
309
328
self.assertEqual(200, response.status_code)
311
330
def test_charms_text_search(self):
312
331
abc_charm = self.makeCharm(summary='abc def')[1]
313
332
self.makeCharm(summary='ghi jkl')[1]
314
response = self.get_response('charms', text='def')
333
response = self.get_response(self.search_endpoint, text='def')
315
334
self.assertEqual(200, response.status_code)
316
335
charms = response.json_body['result']
317
336
self.assertEqual(1, len(charms))
331
350
def test_charms_limit(self):
332
351
"""Limit provides a maximum for charm numbers."""
333
response = self.get_response('charms', limit='6')
352
response = self.get_response(self.search_endpoint, limit='6')
334
353
self.assertEqual(0, len(response.json_body['result']))
336
355
self.makeCharm(owner='jrandom')
337
response = self.get_response('charms', limit='6')
356
response = self.get_response(self.search_endpoint, limit='6')
338
357
self.assertEqual(2, len(response.json_body['result']))
339
response = self.get_response('charms', limit='1')
358
response = self.get_response(self.search_endpoint, limit='1')
340
359
self.assertEqual(1, len(response.json_body['result']))
341
response = self.get_response('charms', limit='0')
360
response = self.get_response(self.search_endpoint, limit='0')
342
361
self.assertEqual(0, len(response.json_body['result']))
343
response = self.get_response('charms', limit='-1')
362
response = self.get_response(self.search_endpoint, limit='-1')
344
363
self.assertEqual(406, response.status_code)
345
364
self.assertEqual({
346
365
'type': 'negative_value',
362
381
dt = datetime.utcfromtimestamp(0)
363
382
last_featured_charm = self.makeCharm(
364
383
name='featured%d' % num, date_created=dt, is_featured=True)[1]
365
res = self.get_response('charms', remainder='/interesting')
384
res = self.get_response(self.search_endpoint, remainder='/interesting')
366
385
data = res.json_body
367
386
self.assertIn('result', data)
368
387
self.assertIn('new', data['result'])
390
409
def test_charms_accepts_categories(self):
391
410
"""Categories are accepted (but ignored)."""
392
response = self.get_response('charms', categories='asdf')
411
response = self.get_response(self.search_endpoint, categories='asdf')
393
412
self.assertEqual(200, response.status_code)
395
414
provides2 = {'cookie-factory': {
411
430
def test_charms_denies_category(self):
412
431
"""Category is not accepted."""
413
response = self.get_response('charms', category='asdf')
432
response = self.get_response(self.search_endpoint, category='asdf')
414
433
self.assertEqual(406, response.status_code)
416
435
def test_charms(self):
448
467
date_created=datetime(1996, 9, 28,
450
469
categories=['cache-proxy'])
451
response = self.get_response('charms')
470
response = self.get_response(self.search_endpoint)
452
471
self.assertEqual(200, response.status_code)
453
472
self.assertEqual('application/json', response.content_type)
454
473
result = response.json_body['result']
463
482
def test_charms_path_notfound(self):
464
483
"""Charms endpoint raises HTTPNotFound for unsupported paths."""
465
484
with self.assertRaises(HTTPNotFound):
466
self.get_response('charms', 'precise/bong-2')
485
self.get_response(self.search_endpoint, 'precise/bong-2')
468
487
def test_autocomplete_matches_on_names(self):
469
488
matching_charm = Charm(self.makeCharm(name='foo')[1])
477
496
differing_charm[field] = 'foo'
478
497
self.index_client.index_charm(differing_charm)
479
498
response = self.get_response(
480
'charms', autocomplete='true', text='foo')
499
self.search_endpoint, autocomplete='true', text='foo')
481
500
ids = [result['charm']['id'] for result in
482
501
response.json_body['result']]
483
502
self.assertEqual([matching_id], ids)
659
680
date_created=older, name='provides-feat', provides={
660
681
'asdf': {'interface': 'feat-r'}
662
data = self.get_response('charms', remainder='/interesting').json_body
683
data = self.get_response(
684
self.search_endpoint, remainder='/interesting').json_body
663
685
self.assertNotIn('related', data['result']['new'][0]['metadata'])
664
686
self.assertNotIn('related', data['result']['popular'][0]['metadata'])
665
687
self.assertNotIn('related', data['result']['featured'][0]['metadata'])
669
691
# first_change and last_change. The view is nevertheless properly
671
693
self.makeCharm(empty_branch=True)
672
result = json.loads(self.get_response('charms').body)['result'][0]
695
self.get_response(self.search_endpoint).body)['result'][0]
673
696
# But the returned data does contain only dummy revision related
675
698
self.assertEqual('0', result['charm']['code_source']['revision'])
687
710
class TestAPIBundles:
689
712
def makeBundle(self, *args, **kwargs):
690
bundle, _ = factory.makeBundle(self.db, *args, **kwargs)
713
bundle, ignore = factory.makeBundle(self.db, *args, **kwargs)
693
716
def test_default_bundle(self):
803
826
def test_unsupported_parameter(self):
804
827
"""Unsupported parameters cause a 406."""
805
response = self.get_response('charms', kermit='6')
828
response = self.get_response(self.search_endpoint, kermit='6')
806
829
self.assertEqual(406, response.status_code)
807
830
self.assertEqual({
808
831
'type': 'parameter_not_supported',
1220
1243
# OPTIONS should return as fast as possible-- it should not search.
1221
1244
self.index_client = FailIndexClient
1222
request = self.make_request('charms', '')
1245
request = self.make_request(self.search_endpoint, '')
1223
1246
request.method = 'OPTIONS'
1224
1247
response = self.api_class(request)()
1225
1248
# Only the headers matter for a CORS preflight response.
1265
1288
self.assertEqual(52, formatted['downloads_in_past_30_days'])
1267
1290
def test_format_related_commits_in_past_30_days(self):
1268
now = datetime(2013, 06, 17)
1291
now = datetime(2013, 6, 17)
1269
1292
charm = factory.get_charm_json(
1270
1293
changes=[factory.makeChange(created=now.date())
1271
1294
for x in range(27)])
1272
1295
formatted = self.api_class._format_related(charm, 0, _now=now)
1273
1296
self.assertEqual(27, formatted['commits_in_past_30_days'])
1274
1297
charm = factory.get_charm_json(
1275
changes=[factory.makeChange(created=date(2013, 05, 18))
1298
changes=[factory.makeChange(created=date(2013, 5, 18))
1276
1299
for x in range(32)] +
1277
[factory.makeChange(created=date(2013, 05, 17))])
1300
[factory.makeChange(created=date(2013, 5, 17))])
1278
1301
formatted = self.api_class._format_related(
1279
1302
charm, 0, _now=now)
1280
1303
self.assertEqual(32, formatted['commits_in_past_30_days'])
1416
1439
class TestAPI3(TestAPI, API3Mixin):
1417
1440
"""Test API 3."""
1442
def test_charms_path_notfound(self):
1443
"""The charms endpoint was removed."""
1444
with self.assertRaises(HTTPNotFound):
1445
self.get_response('charms', None)
1446
with self.assertRaises(HTTPNotFound):
1447
self.get_response('charms', 'precise/bong-2')
1449
def test_search_results_contain_charms_and_bundles(self):
1450
self.use_index_client()
1451
bundle = self.make_indexed_bundle(name='fan-stack-tic')
1452
charm = self.make_indexed_charm()
1453
result = self.get_response(self.search_endpoint).json_body['result']
1454
self.assertEqual(2, len(result))
1455
found_bundle = result[0]
1456
self.assertEqual(BUNDLE, found_bundle['metadata']['doctype'])
1457
self.assertEqual(bundle.name, found_bundle['bundle']['name'])
1458
found_charm = result[1]
1459
self.assertEqual(CHARM, found_charm['metadata']['doctype'])
1460
self.assertEqual(charm['store_url'], found_charm['charm']['url'])
1420
1463
class TestAPI2(TestAPI, API2Mixin):
1421
1464
"""Test API 2."""
1466
def test_charms_passes_doctype(self):
1467
called = dict(kwargs=None)
1470
def fake_search(*args, **kwargs):
1471
called['kwargs'] = kwargs
1472
return {'result': []}
1476
with patch.object(self.api_class, '_search', new_callable=callme):
1477
self.get_response(self.search_endpoint, limit=[5])
1479
{'doctype': CHARM, 'limit': [5]}, called['kwargs'])
1481
def test_charms_results_only_contain_charms(self):
1482
self.use_index_client()
1483
self.make_indexed_bundle()
1484
charm = self.make_indexed_charm()
1485
result = self.get_response(self.search_endpoint).json_body['result']
1486
self.assertEqual(1, len(result))
1487
self.assertEqual(charm['store_url'], result[0]['charm']['url'])