def assert_supersearch_no_errors(): """Make sure an uncached SuperSearch query doesn't have any errors""" supersearch = SuperSearch() # We don't want any caching this time supersearch.cache_seconds = 0 results = supersearch.get( product=settings.DEFAULT_PRODUCT, _results_number=1, _columns=['uuid'], _facets_size=1, ) assert not results['errors'], results['errors']
def test_topcrasher_modes(self, rpost): rpost.side_effect = mocked_post_123 def mocked_supersearch_get(**params): return {'hits': [], 'facets': {'signature': []}, 'total': 0} SuperSearch.implementation().get.side_effect = mocked_supersearch_get now = datetime.datetime.utcnow() today = now.replace(hour=0, minute=0, second=0, microsecond=0) timestr = '%Y-%m-%d %H:%M:%S' now = now.strftime(timestr) today = today.strftime(timestr) with freezegun.freeze_time(now, tz_offset=0): # By default, it returns "real-time" data. response = self.client.get(self.base_url, { 'product': 'WaterWolf', 'version': '19.0', }) eq_(response.status_code, 200) ok_(now in response.content, now) ok_(today not in response.content) # Now test the "day time" data. response = self.client.get(self.base_url, { 'product': 'WaterWolf', 'version': '19.0', '_tcbs_mode': 'byday', }) eq_(response.status_code, 200) ok_(today in response.content) ok_(now not in response.content)
def test_healthcheck(self, mocked_elasticsearch, rget): searches = [] def mocked_supersearch_get(**params): searches.append(params) eq_(params['product'], [settings.DEFAULT_PRODUCT]) eq_(params['_results_number'], 1) eq_(params['_columns'], ['uuid']) return { 'hits': [ {'uuid': '12345'}, ], 'facets': [], 'total': 30002, 'errors': [], } SuperSearch.implementation().get.side_effect = ( mocked_supersearch_get ) def mocked_requests_get(url, **params): return Response(True) rget.side_effect = mocked_requests_get url = reverse('monitoring:healthcheck') response = self.client.get(url) eq_(response.status_code, 200) eq_(json.loads(response.content)['ok'], True) assert len(searches) == 1
def test_assert_supersearch_errors(self): searches = [] def mocked_supersearch_get(**params): searches.append(params) eq_(params['product'], [settings.DEFAULT_PRODUCT]) eq_(params['_results_number'], 1) eq_(params['_columns'], ['uuid']) return { 'hits': [ {'uuid': '12345'}, ], 'facets': [], 'total': 320, 'errors': ['bad'], } SuperSearch.implementation().get.side_effect = ( mocked_supersearch_get ) assert_raises( AssertionError, assert_supersearch_no_errors ) assert len(searches) == 1
def test_heartbeat(self, mocked_elasticsearch, rget): searches = [] def mocked_supersearch_get(**params): searches.append(params) assert params["product"] == [settings.DEFAULT_PRODUCT] assert params["_results_number"] == 1 assert params["_columns"] == ["uuid"] return { "hits": [{ "uuid": "12345" }], "facets": [], "total": 30002, "errors": [], } SuperSearch.implementation().get.side_effect = mocked_supersearch_get def mocked_requests_get(url, **params): return Response(True) rget.side_effect = mocked_requests_get # Verify the __heartbeat__ endpoint url = reverse("monitoring:dockerflow_heartbeat") response = self.client.get(url) assert response.status_code == 200 assert json.loads(response.content)["ok"] is True assert len(searches) == 1
def test_parameters(self): def mocked_supersearch_get(**params): # Verify that all expected parameters are in the URL. ok_("product" in params) ok_("WaterWolf" in params["product"]) ok_("NightTrain" in params["product"]) ok_("address" in params) ok_("0x0" in params["address"]) ok_("0xa" in params["address"]) ok_("reason" in params) ok_("^hello" in params["reason"]) ok_("$thanks" in params["reason"]) ok_("java_stack_trace" in params) ok_("Exception" in params["java_stack_trace"]) return {"hits": [], "facets": "", "total": 0} SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse("signature:signature_reports") response = self.client.get( url, { "signature": DUMB_SIGNATURE, "product": ["WaterWolf", "NightTrain"], "address": ["0x0", "0xa"], "reason": ["^hello", "$thanks"], "java_stack_trace": "Exception", }, ) eq_(response.status_code, 200)
def test_parameters(self): def mocked_supersearch_get(**params): # Verify that all expected parameters are in the URL. ok_('product' in params) ok_('WaterWolf' in params['product']) ok_('NightTrain' in params['product']) ok_('address' in params) ok_('0x0' in params['address']) ok_('0xa' in params['address']) ok_('reason' in params) ok_('^hello' in params['reason']) ok_('$thanks' in params['reason']) ok_('java_stack_trace' in params) ok_('Exception' in params['java_stack_trace']) return {"hits": [], "facets": "", "total": 0} SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('signature:signature_reports') response = self.client.get( url, { 'signature': DUMB_SIGNATURE, 'product': ['WaterWolf', 'NightTrain'], 'address': ['0x0', '0xa'], 'reason': ['^hello', '$thanks'], 'java_stack_trace': 'Exception', }) eq_(response.status_code, 200)
def test_healthcheck(self, mocked_elasticsearch, rget): searches = [] def mocked_supersearch_get(**params): searches.append(params) assert params['product'] == [settings.DEFAULT_PRODUCT] assert params['_results_number'] == 1 assert params['_columns'] == ['uuid'] return { 'hits': [ {'uuid': '12345'}, ], 'facets': [], 'total': 30002, 'errors': [], } SuperSearch.implementation().get.side_effect = mocked_supersearch_get def mocked_requests_get(url, **params): return Response(True) rget.side_effect = mocked_requests_get url = reverse('monitoring:healthcheck') response = self.client.get(url) assert response.status_code == 200 assert json.loads(response.content)['ok'] is True assert len(searches) == 1
def test_topcrasher_modes(self, rpost): rpost.side_effect = mocked_post_123 def mocked_supersearch_get(**params): return {"hits": [], "facets": {"signature": []}, "total": 0} SuperSearch.implementation().get.side_effect = mocked_supersearch_get now = datetime.datetime.utcnow() today = now.replace(hour=0, minute=0, second=0, microsecond=0) timestr = "%Y-%m-%d %H:%M:%S" now = now.strftime(timestr) today = today.strftime(timestr) with freezegun.freeze_time(now, tz_offset=0): # By default, it returns "real-time" data. response = self.client.get(self.base_url, {"product": "WaterWolf", "version": "19.0"}) eq_(response.status_code, 200) ok_(now in response.content, now) ok_(today not in response.content) # Now test the "day time" data. response = self.client.get( self.base_url, {"product": "WaterWolf", "version": "19.0", "_tcbs_mode": "byday"} ) eq_(response.status_code, 200) ok_(today in response.content) ok_(now not in response.content)
def supersearch_field_update(request): field_data = _get_supersearch_field_data(request.POST) if isinstance(field_data, basestring): return http.HttpResponseBadRequest(field_data) api = SuperSearchField() api.update_field(**field_data) SuperSearch.clear_implementations_cache() log(request.user, 'supersearch_field.put', field_data) # Refresh the cache for the fields service. SuperSearchFields().get(refresh_cache=True) return redirect(reverse('manage:supersearch_fields'))
def test_search_results_pagination(self, rpost): """Test that the pagination of results works as expected. """ def mocked_post(**options): assert 'bugs' in options['url'], options['url'] return Response(""" {"hits": [], "total": 0} """) rpost.side_effect = mocked_post def mocked_supersearch_get(**params): assert '_columns' in params # Make sure a negative page does not lead to negative offset value. # But instead it is considered as the page 1 and thus is not added. ok_('_results_offset' not in params) hits = [] for i in range(140): hits.append({ "signature": "nsASDOMWindowEnumerator::GetNext()", "date": "2017-01-31T23:12:57", "uuid": i, "product": "WaterWolf", "version": "1.0", "platform": "Linux", "build_id": 888981 }) return { "hits": self.only_certain_columns(hits, params['_columns']), "facets": "", "total": len(hits) } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('supersearch.search_results') response = self.client.get(url, { '_columns': ['version'], '_facets': ['platform'] }) eq_(response.status_code, 200) ok_('140' in response.content) # Check that the pagination URL contains all three expected parameters. doc = pyquery.PyQuery(response.content) next_page_url = str(doc('.pagination a').eq(0)) ok_('_facets=platform' in next_page_url) ok_('_columns=version' in next_page_url) ok_('page=2' in next_page_url) ok_('#crash-reports' in next_page_url) # Test that a negative page value does not break it. response = self.client.get(url, {'page': '-1'}) eq_(response.status_code, 200)
def supersearch_field_delete(request): field_name = request.GET.get('name') if not field_name: return http.HttpResponseBadRequest('A "name" is needed') api = SuperSearchField() api.delete_field(name=field_name) SuperSearch.clear_implementations_cache() log(request.user, 'supersearch_field.delete', {'name': field_name}) # Refresh the cache for the fields service. SuperSearchFields().get(refresh_cache=True) url = reverse('manage:supersearch_fields') return redirect(url)
def test_signature_reports_pagination(self): """Test that the pagination of results works as expected. """ def mocked_supersearch_get(**params): assert '_columns' in params # Make sure a negative page does not lead to negative offset value. # But instead it is considered as the page 1 and thus is not added. eq_(params.get('_results_offset'), 0) hits = [] for i in range(140): hits.append({ "signature": "nsASDOMWindowEnumerator::GetNext()", "date": "2017-01-31T23:12:57", "uuid": i, "product": "WaterWolf", "version": "1.0", "platform": "Linux", "build_id": 888981 }) return { "hits": self.only_certain_columns(hits, params['_columns']), "facets": "", "total": len(hits) } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('signature:signature_reports') response = self.client.get( url, { 'signature': DUMB_SIGNATURE, 'product': ['WaterWolf'], '_columns': ['platform'] } ) eq_(response.status_code, 200) ok_('140' in response.content) # Check that the pagination URL contains all three expected parameters. doc = pyquery.PyQuery(response.content) next_page_url = str(doc('.pagination a').eq(0)) ok_('product=WaterWolf' in next_page_url) ok_('_columns=platform' in next_page_url) ok_('page=2' in next_page_url) # Test that a negative page value does not break it. response = self.client.get(url, { 'signature': DUMB_SIGNATURE, 'page': '-1', }) eq_(response.status_code, 200)
def test_signature_graphs(self): def mocked_supersearch_get(**params): ok_("signature" in params) eq_(params["signature"], ["=" + DUMB_SIGNATURE]) ok_("_histogram.date" in params) ok_("_facets" in params) if "product" in params["_facets"]: return { "hits": [], "total": 4, "facets": { "product": [{"count": 4, "term": "WaterWolf"}], "histogram_date": [ { "count": 2, "term": "2015-08-05T00:00:00+00:00", "facets": {"product": [{"count": 2, "term": "WaterWolf"}]}, }, { "count": 2, "term": "2015-08-06T00:00:00+00:00", "facets": {"product": [{"count": 2, "term": "WaterWolf"}]}, }, ], }, } return {"hits": [], "total": 0, "facets": {"platform": [], "signature": [], "histogram_date": []}} SuperSearch.implementation().get.side_effect = mocked_supersearch_get # Test with no results url = reverse("signature:signature_graphs", args=("platform",)) response = self.client.get(url, {"signature": DUMB_SIGNATURE}) eq_(response.status_code, 200) ok_("application/json" in response["content-type"]) struct = json.loads(response.content) ok_("aggregates" in struct) eq_(len(struct["aggregates"]), 0) ok_("term_counts" in struct) eq_(len(struct["term_counts"]), 0) # Test with results url = reverse("signature:signature_graphs", args=("product",)) response = self.client.get(url, {"signature": DUMB_SIGNATURE}) eq_(response.status_code, 200) ok_("application/json" in response["content-type"]) struct = json.loads(response.content) ok_("aggregates" in struct) eq_(len(struct["aggregates"]), 2) ok_("term_counts" in struct) eq_(len(struct["term_counts"]), 1)
def test_signature_comments_pagination(self): """Test that the pagination of comments works as expected. """ def mocked_supersearch_get(**params): assert '_columns' in params if params.get('_results_offset') != 0: hits_range = range(100, 140) else: hits_range = range(100) hits = [] for i in hits_range: hits.append({ "date": "2017-01-31T23:12:57", "uuid": i, "user_comments": "hi", }) return { "hits": self.only_certain_columns(hits, params['_columns']), "total": 140 } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('signature:signature_comments') response = self.client.get( url, { 'signature': DUMB_SIGNATURE, 'product': ['WaterWolf'], } ) eq_(response.status_code, 200) ok_('140' in response.content) ok_('99' in response.content) ok_('139' not in response.content) # Check that the pagination URL contains all expected parameters. doc = pyquery.PyQuery(response.content) next_page_url = str(doc('.pagination a').eq(0)) ok_('product=WaterWolf' in next_page_url) ok_('page=2' in next_page_url) response = self.client.get(url, { 'signature': DUMB_SIGNATURE, 'page': '2', }) eq_(response.status_code, 200) ok_('140' in response.content) ok_('99' not in response.content) ok_('139' in response.content)
def supersearch_field_create(request): field_data = _get_supersearch_field_data(request.POST) if isinstance(field_data, basestring): return http.HttpResponseBadRequest(field_data) api = SuperSearchField() api.create_field(**field_data) log(request.user, 'supersearch_field.post', field_data) # Refresh the cache for the fields service. SuperSearchFields().get(refresh_cache=True) SuperSearch.clear_implementations_cache() # The API is using cache to get all fields by a specific namespace # for the whitelist lookup, clear that cache too. cache.delete('api_supersearch_fields_%s' % field_data['namespace']) return redirect(reverse('manage:supersearch_fields'))
def test_search_custom(self): def mocked_supersearch_get(**params): return None SuperSearch.implementation().get.side_effect = mocked_supersearch_get self.create_custom_query_perm() url = reverse('supersearch.search_custom') response = self.client.get(url) eq_(response.status_code, 200) ok_('Run a search to get some results' in response.content)
def test_signature_aggregation(self): def mocked_supersearch_get(**params): ok_('signature' in params) eq_(params['signature'], ['=' + DUMB_SIGNATURE]) ok_('_facets' in params) if 'product' in params['_facets']: return { "hits": [], "facets": { "product": [ { "term": "windows", "count": 42, }, { "term": "linux", "count": 1337, }, { "term": "mac", "count": 3, }, ] }, "total": 1382 } # the default return {"hits": [], "facets": {"platform": []}, "total": 0} SuperSearch.implementation().get.side_effect = mocked_supersearch_get # Test with no results. url = reverse('signature:signature_aggregation', args=('platform', )) response = self.client.get(url, {'signature': DUMB_SIGNATURE}) eq_(response.status_code, 200) ok_('Product' not in response.content) ok_('No results were found' in response.content) # Test with results. url = reverse('signature:signature_aggregation', args=('product', )) response = self.client.get(url, {'signature': DUMB_SIGNATURE}) eq_(response.status_code, 200) ok_('Product' in response.content) ok_('1337' in response.content) ok_('linux' in response.content) ok_(str(1337 / 1382 * 100) in response.content) ok_('windows' in response.content) ok_('mac' in response.content)
def test_signature_summary_with_many_hexes(self, rget): def mocked_get(url, params, **options): if '/graphics_devices' in url: ok_(len(params['vendor_hex']) <= 50) ok_(len(params['adapter_hex']) <= 50) return Response({'hits': [], 'total': 0}) raise NotImplementedError(url) rget.side_effect = mocked_get def mocked_supersearch_get(**params): ok_('signature' in params) eq_(params['signature'], ['=' + DUMB_SIGNATURE]) adapters = [{ 'term': '0x{0:0>4}'.format(i), 'count': 1 } for i in range(50)] vendors = [{ 'term': '0x{0:0>4}'.format(i), 'count': 50, 'facets': { 'adapter_device_id': adapters } } for i in range(3)] res = { 'hits': [], 'total': 4, 'facets': { 'adapter_vendor_id': vendors, } } return res SuperSearch.implementation().get.side_effect = (mocked_supersearch_get) # Test with no results url = reverse('signature:signature_summary') response = self.client.get( url, { 'signature': DUMB_SIGNATURE, 'product': 'WaterWolf', 'version': '1.0', }) eq_(response.status_code, 200) # There are 150 different hexes, there should be 3 calls to the API. eq_(rget.call_count, 3)
def test_signature_comments_pagination(self): """Test that the pagination of comments works as expected. """ def mocked_supersearch_get(**params): assert '_columns' in params if '_results_offset' in params: hits_range = range(100, 140) else: hits_range = range(100) hits = [] for i in hits_range: hits.append({ "date": "2017-01-31T23:12:57", "uuid": i, "user_comments": "hi", }) return { "hits": self.only_certain_columns(hits, params['_columns']), "total": 140 } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('signature:signature_comments') response = self.client.get(url, { 'signature': DUMB_SIGNATURE, 'product': ['WaterWolf'], }) eq_(response.status_code, 200) ok_('140' in response.content) ok_('99' in response.content) ok_('139' not in response.content) # Check that the pagination URL contains all expected parameters. doc = pyquery.PyQuery(response.content) next_page_url = str(doc('.pagination a').eq(0)) ok_('product=WaterWolf' in next_page_url) ok_('page=2' in next_page_url) response = self.client.get(url, { 'signature': DUMB_SIGNATURE, 'page': '2', }) eq_(response.status_code, 200) ok_('140' in response.content) ok_('99' not in response.content) ok_('139' in response.content)
def test_search_results_parameters(self, rpost): def mocked_post(**options): assert 'bugs' in options['url'], options['url'] return Response({ "hits": [], "total": 0 }) rpost.side_effect = mocked_post def mocked_supersearch_get(**params): # Verify that all expected parameters are in the URL. ok_('product' in params) ok_('WaterWolf' in params['product']) ok_('NightTrain' in params['product']) ok_('address' in params) ok_('0x0' in params['address']) ok_('0xa' in params['address']) ok_('reason' in params) ok_('^hello' in params['reason']) ok_('$thanks' in params['reason']) ok_('java_stack_trace' in params) ok_('Exception' in params['java_stack_trace']) return { "hits": [], "facets": "", "total": 0 } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('supersearch.search_results') response = self.client.get( url, { 'product': ['WaterWolf', 'NightTrain'], 'address': ['0x0', '0xa'], 'reason': ['^hello', '$thanks'], 'java_stack_trace': 'Exception', } ) eq_(response.status_code, 200)
def test_topcrasher_without_any_signatures(self, rget, rpost): url = self.base_url + '?product=WaterWolf&version=19.0' response = self.client.get(self.base_url, { 'product': 'WaterWolf', }) ok_(url in response['Location']) rpost.side_effect = mocked_post_123 def mocked_get(url, params, **options): if '/products' in url: return Response(""" { "hits": [ { "is_featured": true, "throttle": 1.0, "end_date": "string", "start_date": "integer", "build_type": "string", "product": "WaterWolf", "version": "19.0", "has_builds": true }], "total": "1" } """) raise NotImplementedError(url) rget.side_effect = mocked_get def mocked_supersearch_get(**params): return { 'hits': [], 'facets': { 'signature': [] }, 'total': 0 } SuperSearch.implementation().get.side_effect = mocked_supersearch_get response = self.client.get(self.base_url, { 'product': 'WaterWolf', 'version': '19.0', }) eq_(response.status_code, 200)
def test_search_results_ratelimited(self): def mocked_supersearch_get(**params): return {"hits": [], "facets": [], "total": 0} SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('supersearch.search_results') limit = int(re.findall('(\d+)', settings.RATELIMIT_SUPERSEARCH)[0]) params = {'product': 'WaterWolf'} # double to avoid https://bugzilla.mozilla.org/show_bug.cgi?id=1148470 for i in range(limit * 2): self.client.get(url, params) response = self.client.get(url, params, HTTP_X_REQUESTED_WITH='XMLHttpRequest') eq_(response.status_code, 429) eq_(response.content, 'Too Many Requests') eq_(response['content-type'], 'text/plain')
def test_signature_aggregation(self): def mocked_supersearch_get(**params): ok_("signature" in params) eq_(params["signature"], ["=" + DUMB_SIGNATURE]) ok_("_facets" in params) if "product" in params["_facets"]: return { "hits": [], "facets": { "product": [ {"term": "windows", "count": 42}, {"term": "linux", "count": 1337}, {"term": "mac", "count": 3}, ] }, "total": 1382, } # the default return {"hits": [], "facets": {"platform": []}, "total": 0} SuperSearch.implementation().get.side_effect = mocked_supersearch_get # Test with no results. url = reverse("signature:signature_aggregation", args=("platform",)) response = self.client.get(url, {"signature": DUMB_SIGNATURE}) eq_(response.status_code, 200) ok_("Product" not in response.content) ok_("No results were found" in response.content) # Test with results. url = reverse("signature:signature_aggregation", args=("product",)) response = self.client.get(url, {"signature": DUMB_SIGNATURE}) eq_(response.status_code, 200) ok_("Product" in response.content) ok_("1337" in response.content) ok_("linux" in response.content) ok_(str(1337 / 1382 * 100) in response.content) ok_("windows" in response.content) ok_("mac" in response.content)
def test_assert_supersearch_errors(self): searches = [] def mocked_supersearch_get(**params): searches.append(params) assert params["product"] == [settings.DEFAULT_PRODUCT] assert params["_results_number"] == 1 assert params["_columns"] == ["uuid"] return { "hits": [{"uuid": "12345"}], "facets": [], "total": 320, "errors": ["bad"], } SuperSearch.implementation().get.side_effect = mocked_supersearch_get with pytest.raises(AssertionError): assert_supersearch_no_errors() assert len(searches) == 1
def test_search_results_ratelimited(self): def mocked_supersearch_get(**params): return {"hits": [], "facets": [], "total": 0} SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('supersearch.search_results') limit = int(re.findall('(\d+)', settings.RATELIMIT_SUPERSEARCH)[0]) params = {'product': 'WaterWolf'} # double to avoid https://bugzilla.mozilla.org/show_bug.cgi?id=1148470 for i in range(limit * 2): self.client.get(url, params) response = self.client.get( url, params, HTTP_X_REQUESTED_WITH='XMLHttpRequest' ) eq_(response.status_code, 429) eq_(response.content, 'Too Many Requests') eq_(response['content-type'], 'text/plain')
def test_change_certain_exceptions_to_bad_request(self): # It actually doesn't matter so much which service we use # because we're heavily mocking it. # Here we use the SuperSearch model. def mocked_supersearch_get(**params): if params.get('product'): raise MissingArgumentError(params['product']) else: raise BadArgumentError('That was a bad thing to do') SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('api:model_wrapper', args=('SuperSearch', )) response = self.client.get(url) assert response.status_code == 400 assert 'That was a bad thing to do' in response.content response = self.client.get(url, {'product': 'foobaz'}) assert response.status_code == 400 assert 'foobaz' in response.content
def test_change_certain_exceptions_to_bad_request(self): # It actually doesn't matter so much which service we use # because we're heavily mocking it. # Here we use the SuperSearch model. def mocked_supersearch_get(**params): if params.get('product'): raise MissingArgumentError(params['product']) else: raise BadArgumentError('That was a bad thing to do') SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('api:model_wrapper', args=('SuperSearch',)) response = self.client.get(url) eq_(response.status_code, 400) ok_('That was a bad thing to do' in response.content) response = self.client.get(url, {'product': 'foobaz'}) eq_(response.status_code, 400) ok_('foobaz' in response.content)
def test_topcrasher_modes(self, rpost): rpost.side_effect = mocked_post_123 def mocked_supersearch_get(**params): return { 'hits': [], 'facets': { 'signature': [] }, 'total': 0 } SuperSearch.implementation().get.side_effect = mocked_supersearch_get now = datetime.datetime.utcnow() today = now.replace(hour=0, minute=0, second=0, microsecond=0) timestr = '%Y-%m-%d %H:%M:%S' now = now.strftime(timestr) today = today.strftime(timestr) with freezegun.freeze_time(now, tz_offset=0): # By default, it returns "real-time" data. response = self.client.get(self.base_url, { 'product': 'WaterWolf', 'version': '19.0', }) eq_(response.status_code, 200) ok_(now in response.content, now) ok_(today not in response.content) # Now test the "day time" data. response = self.client.get(self.base_url, { 'product': 'WaterWolf', 'version': '19.0', '_tcbs_mode': 'byday', }) eq_(response.status_code, 200) ok_(today in response.content) ok_(now not in response.content)
def test_parameters(self): def mocked_supersearch_get(**params): # Verify that all expected parameters are in the URL. ok_('product' in params) ok_('WaterWolf' in params['product']) ok_('NightTrain' in params['product']) ok_('address' in params) ok_('0x0' in params['address']) ok_('0xa' in params['address']) ok_('reason' in params) ok_('^hello' in params['reason']) ok_('$thanks' in params['reason']) ok_('java_stack_trace' in params) ok_('Exception' in params['java_stack_trace']) return { "hits": [], "facets": "", "total": 0 } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('signature:signature_reports') response = self.client.get( url, { 'signature': DUMB_SIGNATURE, 'product': ['WaterWolf', 'NightTrain'], 'address': ['0x0', '0xa'], 'reason': ['^hello', '$thanks'], 'java_stack_trace': 'Exception', } ) eq_(response.status_code, 200)
def test_assert_supersearch_errors(self): searches = [] def mocked_supersearch_get(**params): searches.append(params) assert params['product'] == [settings.DEFAULT_PRODUCT] assert params['_results_number'] == 1 assert params['_columns'] == ['uuid'] return { 'hits': [ {'uuid': '12345'}, ], 'facets': [], 'total': 320, 'errors': ['bad'], } SuperSearch.implementation().get.side_effect = mocked_supersearch_get with pytest.raises(AssertionError): assert_supersearch_no_errors() assert len(searches) == 1
def test_search_custom_parameters(self): self.create_custom_query_perm() def mocked_supersearch_get(**params): ok_('_return_query' in params) ok_('signature' in params) eq_(params['signature'], ['nsA']) return { "query": {"query": None}, "indices": ["socorro200000", "socorro200001"] } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('supersearch.search_custom') response = self.client.get(url, {'signature': 'nsA'}) eq_(response.status_code, 200) ok_('Run a search to get some results' in response.content) ok_('{"query": null}' in response.content) ok_('socorro200000' in response.content) ok_('socorro200001' in response.content)
def test_signature_comments_pagination(self): """Test that the pagination of comments works as expected. """ def mocked_supersearch_get(**params): assert "_columns" in params if params.get("_results_offset") != 0: hits_range = range(100, 140) else: hits_range = range(100) hits = [] for i in hits_range: hits.append({"date": "2017-01-31T23:12:57", "uuid": i, "user_comments": "hi"}) return {"hits": self.only_certain_columns(hits, params["_columns"]), "total": 140} SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse("signature:signature_comments") response = self.client.get(url, {"signature": DUMB_SIGNATURE, "product": ["WaterWolf"]}) eq_(response.status_code, 200) ok_("140" in response.content) ok_("99" in response.content) ok_("139" not in response.content) # Check that the pagination URL contains all expected parameters. doc = pyquery.PyQuery(response.content) next_page_url = str(doc(".pagination a").eq(0)) ok_("product=WaterWolf" in next_page_url) ok_("page=2" in next_page_url) response = self.client.get(url, {"signature": DUMB_SIGNATURE, "page": "2"}) eq_(response.status_code, 200) ok_("140" in response.content) ok_("99" not in response.content) ok_("139" in response.content)
def test_healthcheck(self, mocked_elasticsearch, rget): searches = [] def mocked_supersearch_get(**params): searches.append(params) assert params['product'] == [settings.DEFAULT_PRODUCT] assert params['_results_number'] == 1 assert params['_columns'] == ['uuid'] return { 'hits': [ {'uuid': '12345'}, ], 'facets': [], 'total': 30002, 'errors': [], } SuperSearch.implementation().get.side_effect = mocked_supersearch_get def mocked_requests_get(url, **params): return Response(True) rget.side_effect = mocked_requests_get # Verify the __heartbeat__ endpoint url = reverse('monitoring:dockerflow_heartbeat') response = self.client.get(url) assert response.status_code == 200 assert json.loads(response.content)['ok'] is True assert len(searches) == 1 # Verify the deprecated healthcheck endpoint searches = [] url = reverse('monitoring:healthcheck') response = self.client.get(url) assert response.status_code == 200 assert json.loads(response.content)['ok'] is True assert len(searches) == 1
def test_signature_summary_with_many_hexes(self, rget): def mocked_get(url, params, **options): if "/graphics_devices" in url: ok_(len(params["vendor_hex"]) <= 50) ok_(len(params["adapter_hex"]) <= 50) return Response({"hits": [], "total": 0}) raise NotImplementedError(url) rget.side_effect = mocked_get def mocked_supersearch_get(**params): ok_("signature" in params) eq_(params["signature"], ["=" + DUMB_SIGNATURE]) adapters = [{"term": "0x{0:0>4}".format(i), "count": 1} for i in range(50)] vendors = [ {"term": "0x{0:0>4}".format(i), "count": 50, "facets": {"adapter_device_id": adapters}} for i in range(3) ] res = {"hits": [], "total": 4, "facets": {"adapter_vendor_id": vendors}} return res SuperSearch.implementation().get.side_effect = mocked_supersearch_get # Test with no results url = reverse("signature:signature_summary") response = self.client.get(url, {"signature": DUMB_SIGNATURE, "product": "WaterWolf", "version": "1.0"}) eq_(response.status_code, 200) # There are 150 different hexes, there should be 3 calls to the API. eq_(rget.call_count, 3)
def test_signature_graphs(self): def mocked_supersearch_get(**params): ok_('signature' in params) eq_(params['signature'], ['=' + DUMB_SIGNATURE]) ok_('_histogram.date' in params) ok_('_facets' in params) if 'product' in params['_facets']: return { "hits": [], "total": 4, "facets": { "product": [ { "count": 4, "term": "WaterWolf" } ], "histogram_date": [ { "count": 2, "term": "2015-08-05T00:00:00+00:00", "facets": { "product": [ { "count": 2, "term": "WaterWolf" } ] } }, { "count": 2, "term": "2015-08-06T00:00:00+00:00", "facets": { "product": [ { "count": 2, "term": "WaterWolf" } ] } } ] } } return { "hits": [], "total": 0, "facets": { "platform": [], "signature": [], "histogram_date": [] } } SuperSearch.implementation().get.side_effect = mocked_supersearch_get # Test with no results url = reverse( 'signature:signature_graphs', args=('platform',) ) response = self.client.get(url, {'signature': DUMB_SIGNATURE}) eq_(response.status_code, 200) ok_('application/json' in response['content-type']) struct = json.loads(response.content) ok_('aggregates' in struct) eq_(len(struct['aggregates']), 0) ok_('term_counts' in struct) eq_(len(struct['term_counts']), 0) # Test with results url = reverse( 'signature:signature_graphs', args=('product',) ) response = self.client.get(url, {'signature': DUMB_SIGNATURE}) eq_(response.status_code, 200) ok_('application/json' in response['content-type']) struct = json.loads(response.content) ok_('aggregates' in struct) eq_(len(struct['aggregates']), 2) ok_('term_counts' in struct) eq_(len(struct['term_counts']), 1)
def get(self, **kwargs): form = forms.NewSignaturesForm(kwargs) if not form.is_valid(): return http.JsonResponse({"errors": form.errors}, status=400) start_date = form.cleaned_data["start_date"] end_date = form.cleaned_data["end_date"] not_after = form.cleaned_data["not_after"] product = form.cleaned_data[ "product"] or productlib.get_default_product().name # Make default values for all dates parameters. if not end_date: end_date = datetime.datetime.utcnow().date() + datetime.timedelta( days=1) if not start_date: start_date = end_date - datetime.timedelta(days=8) if not not_after: not_after = start_date - datetime.timedelta(days=14) api = SuperSearch() signatures_number = 100 # First let's get a list of the top signatures that appeared during # the period we are interested in. params = { "product": product, "version": form.cleaned_data["version"], "date": [">=" + start_date.isoformat(), "<" + end_date.isoformat()], "_facets": "signature", "_facets_size": signatures_number, "_results_number": 0, } data = api.get(**params) signatures = [] for signature in data["facets"]["signature"]: signatures.append(signature["term"]) # Now we want to verify whether those signatures appeared or not during # some previous period of time. params["date"] = [ ">=" + not_after.isoformat(), "<" + start_date.isoformat() ] # Filter exactly the signatures that we have. params["signature"] = ["=" + x for x in signatures] data = api.get(**params) # If any of those signatures is in the results, it's that it did not # appear during the period of time we are interested in. Let's # remove it from the list of new signatures. for signature in data["facets"]["signature"]: if signature["term"] in signatures: signatures.remove(signature["term"]) # All remaining signatures are "new" ones. return {"hits": signatures, "total": len(signatures)}
def test_signature_comments(self): def mocked_supersearch_get(**params): assert '_columns' in params ok_('signature' in params) eq_(params['signature'], ['=' + DUMB_SIGNATURE]) ok_('user_comments' in params) eq_(params['user_comments'], ['!__null__']) if 'product' in params: results = { "hits": [ { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa1", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "user_comments": "hello there people!", "useragent_locale": "locale1" }, { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa2", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "user_comments": "I love Mozilla", "useragent_locale": "locale2" }, { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa3", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "user_comments": "this product is awesome", "useragent_locale": "locale3" }, { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa4", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "user_comments": "WaterWolf Y U SO GOOD?", "useragent_locale": "locale4" } ], "total": 4 } results['hits'] = self.only_certain_columns( results['hits'], params['_columns'] ) return results return {"hits": [], "total": 0} SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('signature:signature_comments') # Test with no results. response = self.client.get(url, { 'signature': DUMB_SIGNATURE, }) eq_(response.status_code, 200) ok_('Crash ID' not in response.content) ok_('No comments were found' in response.content) # Test with results. response = self.client.get(url, { 'signature': DUMB_SIGNATURE, 'product': 'WaterWolf' }) eq_(response.status_code, 200) ok_('aaaaaaaaaaaaa1' in response.content) ok_('Crash ID' in response.content) ok_('hello there' in response.content) ok_('WaterWolf Y U SO GOOD' in response.content) ok_('locale1' in response.content)
def test_SuperSearch(self): def mocked_supersearch_get(**params): assert 'exploitability' not in params restricted_params = ( '_facets', '_aggs.signature', '_histogram.date', ) for key in restricted_params: if key in params: assert 'url' not in params[key] assert 'email' not in params[key] assert '_cardinality.email' not in params[key] if 'product' in params: assert params['product'] == ['WaterWolf', 'NightTrain'] return { 'hits': [{ 'signature': 'abcdef', 'product': 'WaterWolf', 'version': '1.0', 'email': '*****@*****.**', 'exploitability': 'high', 'url': 'http://embarassing.website.com', 'user_comments': 'hey I am [email protected]', }], 'facets': { 'signature': [] }, 'total': 0 } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('api:model_wrapper', args=('SuperSearch', )) response = self.client.get(url) assert response.status_code == 200 res = json.loads(response.content) assert res['hits'] assert res['facets'] # Verify forbidden fields are not exposed. assert 'email' not in res['hits'] assert 'exploitability' not in res['hits'] assert 'url' not in res['hits'] # Verify user comments are scrubbed. assert '*****@*****.**' not in res['hits'][0]['user_comments'] # Verify it's not possible to use restricted parameters. response = self.client.get( url, { 'exploitability': 'high', '_facets': ['url', 'email', 'product', '_cardinality.email'], '_aggs.signature': ['url', 'email', 'product', '_cardinality.email'], '_histogram.date': ['url', 'email', 'product', '_cardinality.email'], }) assert response.status_code == 200 # Verify values can be lists. response = self.client.get(url, {'product': ['WaterWolf', 'NightTrain']}) assert response.status_code == 200
def test_NewSignatures(self): def mocked_supersearch_get(**params): assert params['product'] == [settings.DEFAULT_PRODUCT] if 'version' in params: assert params['version'] == ['1.0', '2.0'] if 'signature' not in params: # Return a list of signatures. signatures = [ { 'term': 'ba', 'count': 21 }, { 'term': 'zin', 'count': 19 }, { 'term': 'ga', 'count': 1 }, ] else: # Return only some of the above signatures. The missing ones # are "new" signatures. signatures = [ { 'term': 'ga', 'count': 21 }, ] return { 'hits': [], 'facets': { 'signature': signatures }, 'total': 43829, } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('api:model_wrapper', args=('NewSignatures', )) # Test we get expected results. response = self.client.get(url) assert response.status_code == 200 res_expected = [ 'ba', 'zin', ] res = json.loads(response.content) assert res['hits'] == res_expected # Test with versions. response = self.client.get(url, {'version': ['1.0', '2.0']}) assert response.status_code == 200 # Test with incorrect arguments. response = self.client.get( url, { 'start_date': 'not a date', 'end_date': 'not a date', 'not_after': 'not a date', }) assert response.status_code == 400 assert response['Content-Type'] == 'application/json; charset=UTF-8' res = json.loads(response.content) assert 'errors' in res assert len(res['errors']) == 3
def test_signature_summary(self): def mocked_supersearch_get(**params): ok_('signature' in params) eq_(params['signature'], ['=' + DUMB_SIGNATURE]) ok_('_histogram.uptime' in params) ok_('_facets' in params) res = { "hits": [], "total": 4, "facets": { "platform_pretty_version": [ { "count": 4, "term": "WaterWolf" } ], "cpu_name": [ { "count": 4, "term": "x86" } ], "process_type": [ { "count": 4, "term": "browser" } ], "flash_version": [ { "count": 4, "term": "1.1.1.14" } ], "histogram_uptime": [ { "count": 2, "term": 0, }, { "count": 2, "term": 60, } ], } } if '_histogram.date' in params: res['facets']['histogram_date'] = [ { "count": 2, "term": "2015-08-05T00:00:00+00:00", "facets": { "exploitability": [ { "count": 2, "term": "high" } ] } }, { "count": 2, "term": "2015-08-06T00:00:00+00:00", "facets": { "exploitability": [ { "count": 2, "term": "low" } ] } } ] return res SuperSearch.implementation().get.side_effect = ( mocked_supersearch_get ) # Test with no results url = reverse('signature:signature_summary') response = self.client.get(url, {'signature': DUMB_SIGNATURE}) eq_(response.status_code, 200) # Make sure all boxes are there. ok_('Operating System' in response.content) ok_('Uptime Range' in response.content) ok_('Architecture' in response.content) ok_('Process Type' in response.content) ok_('Flash™ Version' in response.content) # Logged out users can't see no exploitability ok_('Exploitability' not in response.content) # Check that some of the expected values are there. ok_('WaterWolf' in response.content) ok_('x86' in response.content) ok_('browser' in response.content) ok_('1.1.1.14' in response.content) ok_('< 1 min' in response.content) ok_('1-5 min' in response.content) user = self._login() response = self.client.get(url, {'signature': DUMB_SIGNATURE}) eq_(response.status_code, 200) # Logged in users without the permission can't see no exploitability ok_('Exploitability' not in response.content) group = self._create_group_with_permission('view_exploitability') user.groups.add(group) assert user.has_perm('crashstats.view_exploitability') response = self.client.get(url, {'signature': DUMB_SIGNATURE}) eq_(response.status_code, 200) # Logged in users with the permission can see exploitability ok_('Exploitability' in response.content)
def test_graphics_report(self): def mocked_supersearch_get(**params): assert params['product'] == [settings.DEFAULT_PRODUCT] hits = [ { 'signature': 'my signature', 'date': '2015-10-08T23:22:21.1234 +00:00', 'cpu_name': 'arm', 'cpu_info': 'ARMv7 ARM', }, { 'signature': 'other signature', 'date': '2015-10-08T13:12:11.1123 +00:00', 'cpu_info': 'something', # note! no cpu_name }, ] # Value for each of these needs to be in there # supplement missing ones from the fixtures we intend to return. for hit in hits: for head in GRAPHICS_REPORT_HEADER: if head not in hit: hit[head] = None return {'hits': hits, 'total': 2} SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('graphics:report') # viewing this report requires that you're signed in response = self.client.get(url) assert response.status_code == 403 # But being signed in isn't good enough, you need the right # permissions too. user = self._login() response = self.client.get(url) assert response.status_code == 403 # Add the user to the Hackers group which has run_long_queries # permission group = Group.objects.get(name='Hackers') user.groups.add(group) # But even with the right permissions you still need to # provide the right minimal parameters. response = self.client.get(url) assert response.status_code == 400 # Let's finally get it right. Permission AND the date parameter. data = {'date': datetime.datetime.utcnow().date()} response = self.client.get(url, data) assert response.status_code == 200 assert response['Content-Type'] == 'text/csv' assert response['Content-Length'] == str(len(response.content)) # the response content should be parseable length = len(response.content) inp = StringIO(response.content) reader = csv.reader(inp, delimiter='\t') lines = list(reader) assert len(lines) == 3 header = lines[0] assert header == list(GRAPHICS_REPORT_HEADER) first = lines[1] assert first[GRAPHICS_REPORT_HEADER.index( 'signature')] == 'my signature' assert first[GRAPHICS_REPORT_HEADER.index( 'date_processed')] == '201510082322' # now fetch it with gzip response = self.client.get(url, data, HTTP_ACCEPT_ENCODING='gzip') assert response.status_code == 200 assert response['Content-Type'] == 'text/csv' assert response['Content-Length'] == str(len(response.content)) assert response['Content-Encoding'] == 'gzip' assert len(response.content) < length
def test_signature_reports(self): def mocked_supersearch_get(**params): assert '_columns' in params ok_('uuid' in params['_columns']) ok_('signature' in params) eq_(params['signature'], ['=' + DUMB_SIGNATURE]) if 'product' in params: results = { "hits": [{ "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa1", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "build_id": 888981 }, { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa2", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "build_id": 888981 }, { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa3", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "build_id": None }, { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa4", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "build_id": None }], "total": 4 } results['hits'] = self.only_certain_columns( results['hits'], params['_columns']) return results return {"hits": [], "total": 0} SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('signature:signature_reports') # Test with no results. response = self.client.get(url, { 'signature': DUMB_SIGNATURE, 'date': '2012-01-01', }) eq_(response.status_code, 200) ok_('table id="reports-list"' not in response.content) ok_('No results were found' in response.content) # Test with results. response = self.client.get(url, { 'signature': DUMB_SIGNATURE, 'product': 'WaterWolf' }) eq_(response.status_code, 200) ok_('table id="reports-list"' in response.content) ok_('aaaaaaaaaaaaa1' in response.content) ok_('888981' in response.content) ok_('Linux' in response.content) ok_('2017-01-31 23:12:57' in response.content) # Test with a different columns list. response = self.client.get( url, { 'signature': DUMB_SIGNATURE, 'product': 'WaterWolf', '_columns': ['build_id', 'platform'], }) eq_(response.status_code, 200) ok_('table id="reports-list"' in response.content) # The build and platform appear ok_('888981' in response.content) ok_('Linux' in response.content) # The crash id is always shown ok_('aaaaaaaaaaaaa1' in response.content) # The version and date do not appear ok_('1.0' not in response.content) ok_('2017' not in response.content) # Test missing parameter. response = self.client.get(url) eq_(response.status_code, 400) response = self.client.get(url, { 'signature': '', }) eq_(response.status_code, 400)
def test_NewSignatures(self): def mocked_supersearch_get(**params): assert params["product"] == [settings.DEFAULT_PRODUCT] if "version" in params: assert params["version"] == ["1.0", "2.0"] if "signature" not in params: # Return a list of signatures. signatures = [ { "term": "ba", "count": 21 }, { "term": "zin", "count": 19 }, { "term": "ga", "count": 1 }, ] else: # Return only some of the above signatures. The missing ones # are "new" signatures. signatures = [{"term": "ga", "count": 21}] return { "hits": [], "facets": { "signature": signatures }, "total": 43829 } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse("api:model_wrapper", args=("NewSignatures", )) # Test we get expected results. response = self.client.get(url) assert response.status_code == 200 res_expected = ["ba", "zin"] res = json.loads(response.content) assert res["hits"] == res_expected # Test with versions. response = self.client.get(url, {"version": ["1.0", "2.0"]}) assert response.status_code == 200 # Test with incorrect arguments. response = self.client.get( url, { "start_date": "not a date", "end_date": "not a date", "not_after": "not a date", }, ) assert response.status_code == 400 assert response["Content-Type"] == "application/json; charset=UTF-8" res = json.loads(response.content) assert "errors" in res assert len(res["errors"]) == 3
def test_signature_reports(self): def mocked_supersearch_get(**params): assert '_columns' in params ok_('uuid' in params['_columns']) ok_('signature' in params) eq_(params['signature'], ['=' + DUMB_SIGNATURE]) if 'product' in params: results = { "hits": [ { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa1", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "build_id": 888981 }, { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa2", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "build_id": 888981 }, { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa3", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "build_id": None }, { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa4", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "build_id": None } ], "total": 4 } results['hits'] = self.only_certain_columns( results['hits'], params['_columns'] ) return results return {"hits": [], "total": 0} SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('signature:signature_reports') # Test with no results. response = self.client.get(url, { 'signature': DUMB_SIGNATURE, 'date': '2012-01-01', }) eq_(response.status_code, 200) ok_('table id="reports-list"' not in response.content) ok_('No results were found' in response.content) # Test with results. response = self.client.get(url, { 'signature': DUMB_SIGNATURE, 'product': 'WaterWolf' }) eq_(response.status_code, 200) ok_('table id="reports-list"' in response.content) ok_('aaaaaaaaaaaaa1' in response.content) ok_('888981' in response.content) ok_('Linux' in response.content) ok_('2017-01-31 23:12:57' in response.content) # Test with a different columns list. response = self.client.get(url, { 'signature': DUMB_SIGNATURE, 'product': 'WaterWolf', '_columns': ['build_id', 'platform'], }) eq_(response.status_code, 200) ok_('table id="reports-list"' in response.content) # The build and platform appear ok_('888981' in response.content) ok_('Linux' in response.content) # The crash id is always shown ok_('aaaaaaaaaaaaa1' in response.content) # The version and date do not appear ok_('1.0' not in response.content) ok_('2017' not in response.content) # Test missing parameter. response = self.client.get(url) eq_(response.status_code, 400) response = self.client.get(url, { 'signature': '', }) eq_(response.status_code, 400)
def graphics_report(request): """Return a CSV output of all crashes for a specific date for a particular day and a particular product.""" if (not request.user.is_active or not request.user.has_perm('crashstats.run_long_queries')): return http.HttpResponseForbidden( "You must have the 'Run long queries' permission") form = forms.GraphicsReportForm(request.GET, ) if not form.is_valid(): return http.HttpResponseBadRequest(str(form.errors)) batch_size = 1000 product = form.cleaned_data['product'] or settings.DEFAULT_PRODUCT date = form.cleaned_data['date'] params = { 'product': product, 'date': [ '>={}'.format(date.strftime('%Y-%m-%d')), '<{}'.format( (date + datetime.timedelta(days=1)).strftime('%Y-%m-%d')) ], '_columns': ( 'signature', 'uuid', 'date', 'product', 'version', 'build_id', 'platform', 'platform_version', 'cpu_name', 'cpu_info', 'address', 'uptime', 'topmost_filenames', 'reason', 'app_notes', 'release_channel', ), '_results_number': batch_size, '_results_offset': 0, } api = SuperSearch() # Do the first query. That'll give us the total and the first page's # worth of crashes. data = api.get(**params) assert 'hits' in data accept_gzip = 'gzip' in request.META.get('HTTP_ACCEPT_ENCODING', '') response = http.HttpResponse(content_type='text/csv') out = BytesIO() writer = utils.UnicodeWriter(out, delimiter='\t') writer.writerow(GRAPHICS_REPORT_HEADER) pages = data['total'] // batch_size # if there is a remainder, add one more page if data['total'] % batch_size: pages += 1 alias = { 'crash_id': 'uuid', 'os_name': 'platform', 'os_version': 'platform_version', 'date_processed': 'date', 'build': 'build_id', 'uptime_seconds': 'uptime', } # Make sure that we don't have an alias for a header we don't need alias_excess = set(alias.keys()) - set(GRAPHICS_REPORT_HEADER) if alias_excess: raise ValueError('Not all keys in the map of aliases are in ' 'the header ({!r})'.format(alias_excess)) def get_value(row, key): """Return the appropriate output from the row of data, one key at a time. The output is what's used in writing the CSV file. The reason for doing these "hacks" is to match what used to be done with the SELECT statement in SQL in the ancient, but now replaced, report. """ value = row.get(alias.get(key, key)) if key == 'cpu_info': value = '{cpu_name} | {cpu_info}'.format( cpu_name=row.get('cpu_name', ''), cpu_info=row.get('cpu_info', ''), ) if value is None: return '' if key == 'date_processed': value = timezone.make_aware( datetime.datetime.strptime( value.split('.')[0], '%Y-%m-%dT%H:%M:%S')) value = value.strftime('%Y%m%d%H%M') if key == 'uptime_seconds' and value == 0: value = '' return value for page in range(pages): if page > 0: params['_results_offset'] = batch_size * page data = api.get(**params) for row in data['hits']: # Each row is a dict, we want to turn it into a list of # exact order as the `header` tuple above. # However, because the csv writer module doesn't "understand" # python's None, we'll replace those with '' to make the # CSV not have the word 'None' where the data is None. writer.writerow( [get_value(row, x) for x in GRAPHICS_REPORT_HEADER]) payload = out.getvalue() if accept_gzip: zbuffer = BytesIO() zfile = gzip.GzipFile(mode='wb', compresslevel=6, fileobj=zbuffer) zfile.write(payload) zfile.close() compressed_payload = zbuffer.getvalue() response.write(compressed_payload) response['Content-Length'] = len(compressed_payload) response['Content-Encoding'] = 'gzip' else: response.write(payload) response['Content-Length'] = len(payload) return response
def test_signature_graphs(self): def mocked_supersearch_get(**params): ok_('signature' in params) eq_(params['signature'], ['=' + DUMB_SIGNATURE]) ok_('_histogram.date' in params) ok_('_facets' in params) if 'product' in params['_facets']: return { "hits": [], "total": 4, "facets": { "product": [{ "count": 4, "term": "WaterWolf" }], "histogram_date": [{ "count": 2, "term": "2015-08-05T00:00:00+00:00", "facets": { "product": [{ "count": 2, "term": "WaterWolf" }] } }, { "count": 2, "term": "2015-08-06T00:00:00+00:00", "facets": { "product": [{ "count": 2, "term": "WaterWolf" }] } }] } } return { "hits": [], "total": 0, "facets": { "platform": [], "signature": [], "histogram_date": [] } } SuperSearch.implementation().get.side_effect = mocked_supersearch_get # Test with no results url = reverse('signature:signature_graphs', args=('platform', )) response = self.client.get(url, {'signature': DUMB_SIGNATURE}) eq_(response.status_code, 200) ok_('application/json' in response['content-type']) struct = json.loads(response.content) ok_('aggregates' in struct) eq_(len(struct['aggregates']), 0) ok_('term_counts' in struct) eq_(len(struct['term_counts']), 0) # Test with results url = reverse('signature:signature_graphs', args=('product', )) response = self.client.get(url, {'signature': DUMB_SIGNATURE}) eq_(response.status_code, 200) ok_('application/json' in response['content-type']) struct = json.loads(response.content) ok_('aggregates' in struct) eq_(len(struct['aggregates']), 2) ok_('term_counts' in struct) eq_(len(struct['term_counts']), 1)
def test_SuperSearch(self): def mocked_supersearch_get(**params): assert 'exploitability' not in params restricted_params = ( '_facets', '_aggs.signature', '_histogram.date', ) for key in restricted_params: if key in params: assert 'url' not in params[key] assert 'email' not in params[key] assert '_cardinality.email' not in params[key] if 'product' in params: assert params['product'] == ['WaterWolf', 'NightTrain'] return { 'hits': [ { 'signature': 'abcdef', 'product': 'WaterWolf', 'version': '1.0', 'email': '*****@*****.**', 'exploitability': 'high', 'url': 'http://embarassing.website.com', 'user_comments': 'hey I am [email protected]', } ], 'facets': { 'signature': [] }, 'total': 0 } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('api:model_wrapper', args=('SuperSearch',)) response = self.client.get(url) assert response.status_code == 200 res = json.loads(response.content) assert res['hits'] assert res['facets'] # Verify forbidden fields are not exposed. assert 'email' not in res['hits'] assert 'exploitability' not in res['hits'] assert 'url' not in res['hits'] # Verify it's not possible to use restricted parameters. response = self.client.get(url, { 'exploitability': 'high', '_facets': ['url', 'email', 'product', '_cardinality.email'], '_aggs.signature': [ 'url', 'email', 'product', '_cardinality.email' ], '_histogram.date': [ 'url', 'email', 'product', '_cardinality.email' ], }) assert response.status_code == 200 # Verify values can be lists. response = self.client.get(url, { 'product': ['WaterWolf', 'NightTrain'] }) assert response.status_code == 200
def test_NewSignatures(self): def mocked_supersearch_get(**params): assert params['product'] == [settings.DEFAULT_PRODUCT] if 'version' in params: assert params['version'] == ['1.0', '2.0'] if 'signature' not in params: # Return a list of signatures. signatures = [ {'term': 'ba', 'count': 21}, {'term': 'zin', 'count': 19}, {'term': 'ga', 'count': 1}, ] else: # Return only some of the above signatures. The missing ones # are "new" signatures. signatures = [ {'term': 'ga', 'count': 21}, ] return { 'hits': [], 'facets': { 'signature': signatures }, 'total': 43829, } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('api:model_wrapper', args=('NewSignatures',)) # Test we get expected results. response = self.client.get(url) assert response.status_code == 200 res_expected = [ 'ba', 'zin', ] res = json.loads(response.content) assert res['hits'] == res_expected # Test with versions. response = self.client.get(url, { 'version': ['1.0', '2.0'] }) assert response.status_code == 200 # Test with incorrect arguments. response = self.client.get(url, { 'start_date': 'not a date', 'end_date': 'not a date', 'not_after': 'not a date', }) assert response.status_code == 400 assert response['Content-Type'] == 'application/json; charset=UTF-8' res = json.loads(response.content) assert 'errors' in res assert len(res['errors']) == 3
def test_signature_comments(self): def mocked_supersearch_get(**params): assert '_columns' in params ok_('signature' in params) eq_(params['signature'], ['=' + DUMB_SIGNATURE]) ok_('user_comments' in params) eq_(params['user_comments'], ['!__null__']) if 'product' in params: results = { "hits": [{ "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa1", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "user_comments": "hello there people!", "useragent_locale": "locale1" }, { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa2", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "user_comments": "I love Mozilla", "useragent_locale": "locale2" }, { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa3", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "user_comments": "this product is awesome", "useragent_locale": "locale3" }, { "date": "2017-01-31T23:12:57", "uuid": "aaaaaaaaaaaaa4", "product": "WaterWolf", "version": "1.0", "platform": "Linux", "user_comments": "WaterWolf Y U SO GOOD?", "useragent_locale": "locale4" }], "total": 4 } results['hits'] = self.only_certain_columns( results['hits'], params['_columns']) return results return {"hits": [], "total": 0} SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse('signature:signature_comments') # Test with no results. response = self.client.get(url, { 'signature': DUMB_SIGNATURE, }) eq_(response.status_code, 200) ok_('Crash ID' not in response.content) ok_('No comments were found' in response.content) # Test with results. response = self.client.get(url, { 'signature': DUMB_SIGNATURE, 'product': 'WaterWolf' }) eq_(response.status_code, 200) ok_('aaaaaaaaaaaaa1' in response.content) ok_('Crash ID' in response.content) ok_('hello there' in response.content) ok_('WaterWolf Y U SO GOOD' in response.content) ok_('locale1' in response.content)
def test_SuperSearch(self): def mocked_supersearch_get(**params): assert "exploitability" not in params restricted_params = ("_facets", "_aggs.signature", "_histogram.date") for key in restricted_params: if key in params: assert "url" not in params[key] assert "email" not in params[key] assert "_cardinality.email" not in params[key] if "product" in params: assert params["product"] == ["WaterWolf", "NightTrain"] return { "hits": [{ "signature": "abcdef", "product": "WaterWolf", "version": "1.0", "email": "*****@*****.**", "exploitability": "high", "url": "http://embarassing.website.com", "user_comments": "hey I am [email protected]", }], "facets": { "signature": [] }, "total": 0, } SuperSearch.implementation().get.side_effect = mocked_supersearch_get url = reverse("api:model_wrapper", args=("SuperSearch", )) response = self.client.get(url) assert response.status_code == 200 res = json.loads(response.content) assert res["hits"] assert res["facets"] # Verify forbidden fields are not exposed. assert "email" not in res["hits"] assert "exploitability" not in res["hits"] assert "url" not in res["hits"] # Verify it's not possible to use restricted parameters. response = self.client.get( url, { "exploitability": "high", "_facets": ["url", "email", "product", "_cardinality.email"], "_aggs.signature": ["url", "email", "product", "_cardinality.email"], "_histogram.date": ["url", "email", "product", "_cardinality.email"], }, ) assert response.status_code == 200 # Verify values can be lists. response = self.client.get(url, {"product": ["WaterWolf", "NightTrain"]}) assert response.status_code == 200
def test_signature_aggregation(self): def mocked_supersearch_get(**params): ok_('signature' in params) eq_(params['signature'], ['=' + DUMB_SIGNATURE]) ok_('_facets' in params) if 'product' in params['_facets']: return { "hits": [], "facets": { "product": [ { "term": "windows", "count": 42, }, { "term": "linux", "count": 1337, }, { "term": "mac", "count": 3, }, ] }, "total": 1382 } # the default return { "hits": [], "facets": { "platform": [] }, "total": 0 } SuperSearch.implementation().get.side_effect = mocked_supersearch_get # Test with no results. url = reverse( 'signature:signature_aggregation', args=('platform',) ) response = self.client.get(url, {'signature': DUMB_SIGNATURE}) eq_(response.status_code, 200) ok_('Product' not in response.content) ok_('No results were found' in response.content) # Test with results. url = reverse( 'signature:signature_aggregation', args=('product',) ) response = self.client.get(url, {'signature': DUMB_SIGNATURE}) eq_(response.status_code, 200) ok_('Product' in response.content) ok_('1337' in response.content) ok_('linux' in response.content) ok_(str(1337 / 1382 * 100) in response.content) ok_('windows' in response.content) ok_('mac' in response.content)