# -*- coding: utf-8 -*-
import json

from nose.tools import eq_, ok_
from rest_framework.exceptions import ParseError

from django.contrib.auth.models import AnonymousUser
from django.test.client import RequestFactory
from django.test.utils import override_settings

import mkt
from mkt.constants.applications import DEVICE_CHOICES_IDS
from mkt.constants.features import FeatureProfile
from mkt.search.filters import (DeviceTypeFilter, ProfileFilter,
                                PublicAppsFilter, PublicSearchFormFilter,
                                RegionFilter, SearchQueryFilter, SortingFilter,
                                ValidAppsFilter)
from mkt.search.forms import TARAKO_CATEGORIES_MAPPING
from mkt.search.views import SearchView
from mkt.site.tests import TestCase
from mkt.webapps.indexers import WebappIndexer


class FilterTestsBase(TestCase):

    def setUp(self):
        super(FilterTestsBase, self).setUp()
        self.req = RequestFactory().get('/')
        self.req.user = AnonymousUser()
        self.view_class = SearchView

    def _filter(self, req=None, data=None):
        req = req or RequestFactory().get('/', data=data or {})
        req.user = AnonymousUser()
        queryset = WebappIndexer.search()
        for filter_class in self.filter_classes:
            queryset = filter_class().filter_queryset(req, queryset,
                                                      self.view_class)
        return queryset.to_dict()


class TestQueryFilter(FilterTestsBase):

    filter_classes = [SearchQueryFilter]

    def test_q(self):
        qs = self._filter(data={'q': 'search terms'})
        # Spot check a few queries.
        should = (qs['query']['function_score']['query']['bool']['should'])
        ok_({'match': {'name': {'query': 'search terms', 'boost': 4,
                                'slop': 1, 'type': 'phrase'}}}
            in should)
        ok_({'prefix': {'name': {'boost': 1.5, 'value': 'search terms'}}}
            in should)
        ok_({'match': {'name_english': {'query': 'search terms',
                                        'boost': 2.5}}}
            in should)
        ok_({'match': {'description_english': {'query': 'search terms',
                                               'boost': 0.6,
                                               'analyzer': 'english_analyzer',
                                               'type': 'phrase'}}}
            in should)

    def test_fuzzy_single_word(self):
        qs = self._filter(data={'q': 'term'})
        should = (qs['query']['function_score']['query']['bool']['should'])
        ok_({'fuzzy': {'tags': {'prefix_length': 1, 'value': 'term'}}}
            in should)

    def test_no_fuzzy_multi_word(self):
        qs = self._filter(data={'q': 'search terms'})
        qs_str = json.dumps(qs)
        ok_('fuzzy' not in qs_str)

    @override_settings(ES_USE_PLUGINS=True)
    def test_polish_analyzer(self):
        """
        Test that the polish analyzer is included correctly since it is an
        exception to the rest b/c it is a plugin.
        """
        with self.activate(locale='pl'):
            qs = self._filter(data={'q': u'próba'})
            should = (qs['query']['function_score']['query']['bool']['should'])
            ok_({'match': {'name_polish': {'query': u'pr\xf3ba',
                                           'boost': 2.5}}}
                in should)
            ok_({'match': {'description_polish': {'query': u'pr\xf3ba',
                                                  'boost': 0.6,
                                                  'analyzer': 'polish',
                                                  'type': 'phrase'}}}
                in should)


class TestFormFilter(FilterTestsBase):

    filter_classes = [PublicSearchFormFilter]

    def test_category(self):
        qs = self._filter(data={'cat': 'games'})
        ok_({'terms': {'category': ['games']}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_tag(self):
        qs = self._filter(data={'tag': 'tarako'})
        ok_({'term': {'tags': 'tarako'}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_tarako_categories(self):
        qs = self._filter(data={'cat': 'tarako-lifestyle'})
        ok_({'terms':
             {'category': TARAKO_CATEGORIES_MAPPING['tarako-lifestyle']}}
            in qs['query']['filtered']['filter']['bool']['must'])

        qs = self._filter(data={'cat': 'tarako-games'})
        ok_({'terms': {'category': TARAKO_CATEGORIES_MAPPING['tarako-games']}}
            in qs['query']['filtered']['filter']['bool']['must'])

        qs = self._filter(data={'cat': 'tarako-tools'})
        ok_({'terms': {'category': TARAKO_CATEGORIES_MAPPING['tarako-tools']}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_app_type(self):
        qs = self._filter(data={'app_type': ['hosted']})
        ok_({'terms': {'app_type': [1]}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_app_type_packaged(self):
        """Test packaged also includes privileged."""
        qs = self._filter(data={'app_type': ['packaged']})
        ok_({'terms': {'app_type': [2, 3]}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_manifest_url(self):
        url = 'http://hy.fr/manifest.webapp'
        qs = self._filter(data={'manifest_url': url})
        ok_({'term': {'manifest_url': url}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_offline(self):
        """Ensure we are filtering by offline-capable apps."""
        qs = self._filter(data={'offline': 'True'})
        ok_({'term': {'is_offline': True}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_online(self):
        """Ensure we are filtering by apps that require online access."""
        qs = self._filter(data={'offline': 'False'})
        ok_({'term': {'is_offline': False}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_offline_and_online(self):
        """Ensure we are not filtering by offline/online by default."""
        # Pass any form values other than 'offline' to create the dict.
        qs = self._filter(data={'cat': 'games'})
        ok_({'term': {'is_offline': True}}
            not in qs['query']['filtered']['filter']['bool']['must'])
        ok_({'term': {'is_offline': False}}
            not in qs['query']['filtered']['filter']['bool']['must'])

    def test_languages(self):
        qs = self._filter(data={'languages': 'fr'})
        ok_({'terms': {'supported_locales': ['fr']}}
            in qs['query']['filtered']['filter']['bool']['must'])

        qs = self._filter(data={'languages': 'ar,en-US'})
        ok_({'terms': {'supported_locales': ['ar', 'en-US']}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_author(self):
        qs = self._filter(data={'author': 'Mozilla LABS'})
        ok_({'term': {'author.raw': u'mozilla labs'}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_installs_allowed_from(self):
        qs = self._filter(data={'installs_allowed_from': '*'})
        ok_({'term': {'installs_allowed_from': u'*'}}
            in qs['query']['filtered']['filter']['bool']['must'])
        # Test that we don't filter by this field if not provided.
        qs = self._filter()
        ok_('installs_allowed_from' not in json.dumps(qs),
            "Unexpected 'installs_allowed_from' in query")

    def test_premium_types(self):
        def ptype(p):
            return mkt.ADDON_PREMIUM_API_LOOKUP.get(p)

        # Test a single premium type.
        qs = self._filter(data={'premium_types': ['free']})
        ok_({'terms': {'premium_type': [ptype('free')]}}
            in qs['query']['filtered']['filter']['bool']['must'])
        # Test many premium types.
        qs = self._filter(data={'premium_types': ['free', 'free-inapp']})
        ok_({'terms': {'premium_type': [ptype('free'), ptype('free-inapp')]}}
            in qs['query']['filtered']['filter']['bool']['must'])
        # Test a non-existent premium type.
        with self.assertRaises(ParseError):
            self._filter(data={'premium_types': ['free', 'platinum']})

    def test_device(self):
        qs = self._filter(data={'dev': 'desktop'})
        ok_({'term': {'device': DEVICE_CHOICES_IDS['desktop']}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_no_device_with_device_type(self):
        """Test that providing a device type w/o device doesn't filter."""
        qs = self._filter(data={'dev': '', 'device': 'firefoxos'})
        ok_('filtered' not in qs['query'].keys())


class TestPublicAppsFilter(FilterTestsBase):

    filter_classes = [PublicAppsFilter]

    def test_status(self):
        qs = self._filter(self.req)
        ok_({'term': {'status': mkt.STATUS_PUBLIC}}
            in qs['query']['filtered']['filter']['bool']['must'])
        ok_({'term': {'is_disabled': False}}
            in qs['query']['filtered']['filter']['bool']['must'])


class TestValidAppsFilter(FilterTestsBase):

    filter_classes = [ValidAppsFilter]

    def test_status(self):
        qs = self._filter(self.req)
        ok_({'terms': {'status': mkt.VALID_STATUSES}}
            in qs['query']['filtered']['filter']['bool']['must'])
        ok_({'term': {'is_disabled': False}}
            in qs['query']['filtered']['filter']['bool']['must'])


class TestDeviceTypeFilter(FilterTestsBase):

    filter_classes = [DeviceTypeFilter]

    def test_no_filters(self):
        qs = self._filter(self.req)
        ok_('filtered' not in qs['query'].keys())

    def test_mobile(self):
        self.req.MOBILE = True
        qs = self._filter(self.req)
        ok_({'term': {'uses_flash': False}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_gaia(self):
        self.req.GAIA = True
        qs = self._filter(self.req)
        ok_({'term': {'uses_flash': False}}
            in qs['query']['filtered']['filter']['bool']['must'])

    def test_tablet(self):
        self.req.TABLET = True
        qs = self._filter(self.req)
        ok_('filtered' not in qs['query'].keys())

    def test_device_in_querystring(self):
        qs = self._filter(data={'dev': 'desktop'})
        ok_({'term': {'device': 1}}
            in qs['query']['filtered']['filter']['bool']['must'])
        qs = self._filter(data={'dev': 'android', 'device': 'mobile'})
        ok_({'term': {'device': 2}}
            in qs['query']['filtered']['filter']['bool']['must'])
        qs = self._filter(data={'dev': 'android', 'device': 'tablet'})
        ok_({'term': {'device': 3}}
            in qs['query']['filtered']['filter']['bool']['must'])
        qs = self._filter(data={'dev': 'firefoxos'})
        ok_({'term': {'device': 4}}
            in qs['query']['filtered']['filter']['bool']['must'])


class TestRegionFilter(FilterTestsBase):

    filter_classes = [RegionFilter]

    def test_no_region_default(self):
        qs = self._filter(self.req)
        ok_({'term': {'region_exclusions': mkt.regions.RESTOFWORLD.id}}
            in qs['query']['filtered']['filter']['bool']['must_not'])

    def test_region(self):
        self.req.REGION = mkt.regions.BRA
        qs = self._filter(self.req)
        ok_({'term': {'region_exclusions': mkt.regions.BRA.id}}
            in qs['query']['filtered']['filter']['bool']['must_not'])


class TestProfileFilter(FilterTestsBase):

    filter_classes = [ProfileFilter]

    def profile_qs(self, disabled_features=None):
        if disabled_features is None:
            disabled_features = {}
        profile = FeatureProfile().fromkeys(FeatureProfile(), True)
        for feature in disabled_features:
            profile[feature] = False
        return {'pro': profile.to_signature(), 'dev': 'firefoxos'}

    def test_filter_all_features_present(self):
        qs = self._filter(data=self.profile_qs())
        ok_('filtered' not in qs['query'].keys())

    def test_filter_one_feature_present(self):
        qs = self._filter(data=self.profile_qs(disabled_features=['sms']))
        ok_({'term': {'features.has_sms': True}}
            in qs['query']['filtered']['filter']['bool']['must_not'])

    def test_filter_one_feature_present_desktop(self):
        data = self.profile_qs(disabled_features=['sms'])
        data['dev'] = 'desktop'
        qs = self._filter(data=data)
        ok_('filtered' not in qs['query'].keys())

    def test_filter_multiple_features_present(self):
        qs = self._filter(
            data=self.profile_qs(disabled_features=['sms', 'apps']))
        ok_({'term': {'features.has_sms': True}}
            in qs['query']['filtered']['filter']['bool']['must_not'])
        ok_({'term': {'features.has_apps': True}}
            in qs['query']['filtered']['filter']['bool']['must_not'])


class TestSortingFilter(FilterTestsBase):

    filter_classes = [SortingFilter]

    def test_sort(self):
        for api_sort, es_sort in SortingFilter.DEFAULT_SORTING.items():
            qs = self._filter(data={'sort': [api_sort]})
            if es_sort.startswith('-'):
                ok_({es_sort[1:]: {'order': 'desc'}} in qs['sort'], qs)
            else:
                eq_([es_sort], qs['sort'], qs)

    def test_sort_multiple(self):
        qs = self._filter(data={'sort': ['rating', 'created']})
        ok_({'bayesian_rating': {'order': 'desc'}} in qs['sort'])
        ok_({'created': {'order': 'desc'}} in qs['sort'])

    def test_sort_regional(self):
        """Popularity and trending use regional sorting for mature regions."""
        req = RequestFactory().get('/')
        req.REGION = mkt.regions.BRA
        # Default empty query searches use popularity.
        qs = self._filter(req)
        ok_({'popularity_%s'
             % mkt.regions.BRA.id: {'order': 'desc'}} in qs['sort'])
        # Popularity.
        req = RequestFactory().get('/', data={'sort': ['popularity']})
        req.REGION = mkt.regions.BRA
        qs = self._filter(req)
        ok_({'popularity_%s'
             % mkt.regions.BRA.id: {'order': 'desc'}} in qs['sort'])
        # Trending.
        req = RequestFactory().get('/', data={'sort': ['trending']})
        req.REGION = mkt.regions.BRA
        qs = self._filter(req)
        ok_({'trending_%s' % mkt.regions.BRA.id: {'order': 'desc'}}
            in qs['sort'])


class TestCombinedFilter(FilterTestsBase):
    """
    Basic test to ensure that when filters are combined they result in the
    expected query structure.

    """
    filter_classes = [SearchQueryFilter, PublicSearchFormFilter,
                      PublicAppsFilter, SortingFilter]

    def test_combined(self):
        qs = self._filter(data={'q': 'test', 'cat': 'games',
                                'sort': 'trending'})
        ok_(qs['query']['filtered']['query']['function_score'])
        ok_(qs['query']['filtered']['filter'])

        must = qs['query']['filtered']['filter']['bool']['must']
        ok_({'terms': {'category': ['games']}} in must)
        ok_({'term': {'status': 4}} in must)
        ok_({'term': {'is_disabled': False}} in must)

        ok_({'trending': {'order': 'desc'}} in qs['sort'])

        query = qs['query']['filtered']['query']
        ok_({'field_value_factor': {'field': 'boost'}}
            in query['function_score']['functions'])
        ok_({'match': {'name_english': {'boost': 2.5, 'query': u'test'}}}
            in query['function_score']['query']['bool']['should'])

[((75, 5, 75, 43), 'django.test.utils.override_settings', 'override_settings', (), '', False, 'from django.test.utils import override_settings\n'), ((29, 24, 29, 39), 'django.contrib.auth.models.AnonymousUser', 'AnonymousUser', ({}, {}), '()', False, 'from django.contrib.auth.models import AnonymousUser\n'), ((34, 19, 34, 34), 'django.contrib.auth.models.AnonymousUser', 'AnonymousUser', ({}, {}), '()', False, 'from django.contrib.auth.models import AnonymousUser\n'), ((35, 19, 35, 41), 'mkt.webapps.indexers.WebappIndexer.search', 'WebappIndexer.search', ({}, {}), '()', False, 'from mkt.webapps.indexers import WebappIndexer\n'), ((50, 8, 52, 22), 'nose.tools.ok_', 'ok_', ({(50, 12, 52, 21): "({'match': {'name': {'query': 'search terms', 'boost': 4, 'slop': 1, 'type':\n    'phrase'}}} in should)"}, {}), "({'match': {'name': {'query': 'search terms', 'boost': 4, 'slop': 1,\n    'type': 'phrase'}}} in should)", False, 'from nose.tools import eq_, ok_\n'), ((53, 8, 54, 22), 'nose.tools.ok_', 'ok_', ({(53, 12, 54, 21): "({'prefix': {'name': {'boost': 1.5, 'value': 'search terms'}}} in should)"}, {}), "({'prefix': {'name': {'boost': 1.5, 'value': 'search terms'}}} in should)", False, 'from nose.tools import eq_, ok_\n'), ((55, 8, 57, 22), 'nose.tools.ok_', 'ok_', ({(55, 12, 57, 21): "({'match': {'name_english': {'query': 'search terms', 'boost': 2.5}}} in should\n    )"}, {}), "({'match': {'name_english': {'query': 'search terms', 'boost': 2.5}}} in\n    should)", False, 'from nose.tools import eq_, ok_\n'), ((58, 8, 62, 22), 'nose.tools.ok_', 'ok_', ({(58, 12, 62, 21): "({'match': {'description_english': {'query': 'search terms', 'boost': 0.6,\n    'analyzer': 'english_analyzer', 'type': 'phrase'}}} in should)"}, {}), "({'match': {'description_english': {'query': 'search terms', 'boost': \n    0.6, 'analyzer': 'english_analyzer', 'type': 'phrase'}}} in should)", False, 'from nose.tools import eq_, ok_\n'), ((67, 8, 68, 22), 'nose.tools.ok_', 'ok_', ({(67, 12, 68, 21): "({'fuzzy': {'tags': {'prefix_length': 1, 'value': 'term'}}} in should)"}, {}), "({'fuzzy': {'tags': {'prefix_length': 1, 'value': 'term'}}} in should)", False, 'from nose.tools import eq_, ok_\n'), ((72, 17, 72, 31), 'json.dumps', 'json.dumps', ({(72, 28, 72, 30): 'qs'}, {}), '(qs)', False, 'import json\n'), ((73, 8, 73, 34), 'nose.tools.ok_', 'ok_', ({(73, 12, 73, 33): "('fuzzy' not in qs_str)"}, {}), "('fuzzy' not in qs_str)", False, 'from nose.tools import eq_, ok_\n'), ((100, 8, 101, 65), 'nose.tools.ok_', 'ok_', ({(100, 12, 101, 64): "({'terms': {'category': ['games']}} in qs['query']['filtered']['filter'][\n    'bool']['must'])"}, {}), "({'terms': {'category': ['games']}} in qs['query']['filtered']['filter']\n    ['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((105, 8, 106, 65), 'nose.tools.ok_', 'ok_', ({(105, 12, 106, 64): "({'term': {'tags': 'tarako'}} in qs['query']['filtered']['filter']['bool'][\n    'must'])"}, {}), "({'term': {'tags': 'tarako'}} in qs['query']['filtered']['filter'][\n    'bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((110, 8, 112, 65), 'nose.tools.ok_', 'ok_', ({(110, 12, 112, 64): "({'terms': {'category': TARAKO_CATEGORIES_MAPPING['tarako-lifestyle']}} in\n    qs['query']['filtered']['filter']['bool']['must'])"}, {}), "({'terms': {'category': TARAKO_CATEGORIES_MAPPING['tarako-lifestyle']}} in\n    qs['query']['filtered']['filter']['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((115, 8, 116, 65), 'nose.tools.ok_', 'ok_', ({(115, 12, 116, 64): "({'terms': {'category': TARAKO_CATEGORIES_MAPPING['tarako-games']}} in qs[\n    'query']['filtered']['filter']['bool']['must'])"}, {}), "({'terms': {'category': TARAKO_CATEGORIES_MAPPING['tarako-games']}} in\n    qs['query']['filtered']['filter']['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((119, 8, 120, 65), 'nose.tools.ok_', 'ok_', ({(119, 12, 120, 64): "({'terms': {'category': TARAKO_CATEGORIES_MAPPING['tarako-tools']}} in qs[\n    'query']['filtered']['filter']['bool']['must'])"}, {}), "({'terms': {'category': TARAKO_CATEGORIES_MAPPING['tarako-tools']}} in\n    qs['query']['filtered']['filter']['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((124, 8, 125, 65), 'nose.tools.ok_', 'ok_', ({(124, 12, 125, 64): "({'terms': {'app_type': [1]}} in qs['query']['filtered']['filter']['bool'][\n    'must'])"}, {}), "({'terms': {'app_type': [1]}} in qs['query']['filtered']['filter'][\n    'bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((130, 8, 131, 65), 'nose.tools.ok_', 'ok_', ({(130, 12, 131, 64): "({'terms': {'app_type': [2, 3]}} in qs['query']['filtered']['filter'][\n    'bool']['must'])"}, {}), "({'terms': {'app_type': [2, 3]}} in qs['query']['filtered']['filter'][\n    'bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((136, 8, 137, 65), 'nose.tools.ok_', 'ok_', ({(136, 12, 137, 64): "({'term': {'manifest_url': url}} in qs['query']['filtered']['filter'][\n    'bool']['must'])"}, {}), "({'term': {'manifest_url': url}} in qs['query']['filtered']['filter'][\n    'bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((142, 8, 143, 65), 'nose.tools.ok_', 'ok_', ({(142, 12, 143, 64): "({'term': {'is_offline': True}} in qs['query']['filtered']['filter']['bool'\n    ]['must'])"}, {}), "({'term': {'is_offline': True}} in qs['query']['filtered']['filter'][\n    'bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((148, 8, 149, 65), 'nose.tools.ok_', 'ok_', ({(148, 12, 149, 64): "({'term': {'is_offline': False}} in qs['query']['filtered']['filter'][\n    'bool']['must'])"}, {}), "({'term': {'is_offline': False}} in qs['query']['filtered']['filter'][\n    'bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((155, 8, 156, 69), 'nose.tools.ok_', 'ok_', ({(155, 12, 156, 68): "({'term': {'is_offline': True}} not in qs['query']['filtered']['filter'][\n    'bool']['must'])"}, {}), "({'term': {'is_offline': True}} not in qs['query']['filtered']['filter']\n    ['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((157, 8, 158, 69), 'nose.tools.ok_', 'ok_', ({(157, 12, 158, 68): "({'term': {'is_offline': False}} not in qs['query']['filtered']['filter'][\n    'bool']['must'])"}, {}), "({'term': {'is_offline': False}} not in qs['query']['filtered']['filter'\n    ]['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((162, 8, 163, 65), 'nose.tools.ok_', 'ok_', ({(162, 12, 163, 64): "({'terms': {'supported_locales': ['fr']}} in qs['query']['filtered'][\n    'filter']['bool']['must'])"}, {}), "({'terms': {'supported_locales': ['fr']}} in qs['query']['filtered'][\n    'filter']['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((166, 8, 167, 65), 'nose.tools.ok_', 'ok_', ({(166, 12, 167, 64): "({'terms': {'supported_locales': ['ar', 'en-US']}} in qs['query'][\n    'filtered']['filter']['bool']['must'])"}, {}), "({'terms': {'supported_locales': ['ar', 'en-US']}} in qs['query'][\n    'filtered']['filter']['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((171, 8, 172, 65), 'nose.tools.ok_', 'ok_', ({(171, 12, 172, 64): "({'term': {'author.raw': u'mozilla labs'}} in qs['query']['filtered'][\n    'filter']['bool']['must'])"}, {}), "({'term': {'author.raw': u'mozilla labs'}} in qs['query']['filtered'][\n    'filter']['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((176, 8, 177, 65), 'nose.tools.ok_', 'ok_', ({(176, 12, 177, 64): "({'term': {'installs_allowed_from': u'*'}} in qs['query']['filtered'][\n    'filter']['bool']['must'])"}, {}), "({'term': {'installs_allowed_from': u'*'}} in qs['query']['filtered'][\n    'filter']['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((201, 8, 202, 65), 'nose.tools.ok_', 'ok_', ({(201, 12, 202, 64): "({'term': {'device': DEVICE_CHOICES_IDS['desktop']}} in qs['query'][\n    'filtered']['filter']['bool']['must'])"}, {}), "({'term': {'device': DEVICE_CHOICES_IDS['desktop']}} in qs['query'][\n    'filtered']['filter']['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((216, 8, 217, 65), 'nose.tools.ok_', 'ok_', ({(216, 12, 217, 64): "({'term': {'status': mkt.STATUS_PUBLIC}} in qs['query']['filtered'][\n    'filter']['bool']['must'])"}, {}), "({'term': {'status': mkt.STATUS_PUBLIC}} in qs['query']['filtered'][\n    'filter']['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((218, 8, 219, 65), 'nose.tools.ok_', 'ok_', ({(218, 12, 219, 64): "({'term': {'is_disabled': False}} in qs['query']['filtered']['filter'][\n    'bool']['must'])"}, {}), "({'term': {'is_disabled': False}} in qs['query']['filtered']['filter'][\n    'bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((228, 8, 229, 65), 'nose.tools.ok_', 'ok_', ({(228, 12, 229, 64): "({'terms': {'status': mkt.VALID_STATUSES}} in qs['query']['filtered'][\n    'filter']['bool']['must'])"}, {}), "({'terms': {'status': mkt.VALID_STATUSES}} in qs['query']['filtered'][\n    'filter']['bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((230, 8, 231, 65), 'nose.tools.ok_', 'ok_', ({(230, 12, 231, 64): "({'term': {'is_disabled': False}} in qs['query']['filtered']['filter'][\n    'bool']['must'])"}, {}), "({'term': {'is_disabled': False}} in qs['query']['filtered']['filter'][\n    'bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((245, 8, 246, 65), 'nose.tools.ok_', 'ok_', ({(245, 12, 246, 64): "({'term': {'uses_flash': False}} in qs['query']['filtered']['filter'][\n    'bool']['must'])"}, {}), "({'term': {'uses_flash': False}} in qs['query']['filtered']['filter'][\n    'bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((251, 8, 252, 65), 'nose.tools.ok_', 'ok_', ({(251, 12, 252, 64): "({'term': {'uses_flash': False}} in qs['query']['filtered']['filter'][\n    'bool']['must'])"}, {}), "({'term': {'uses_flash': False}} in qs['query']['filtered']['filter'][\n    'bool']['must'])", False, 'from nose.tools import eq_, ok_\n'), ((261, 8, 262, 65), 'nose.tools.ok_', 'ok_', ({(261, 12, 262, 64): "({'term': {'device': 1}} in qs['query']['filtered']['filter']['bool']['must'])"}, {}), "({'term': {'device': 1}} in qs['query']['filtered']['filter']['bool'][\n    'must'])", False, 'from nose.tools import eq_, ok_\n'), ((264, 8, 265, 65), 'nose.tools.ok_', 'ok_', ({(264, 12, 265, 64): "({'term': {'device': 2}} in qs['query']['filtered']['filter']['bool']['must'])"}, {}), "({'term': {'device': 2}} in qs['query']['filtered']['filter']['bool'][\n    'must'])", False, 'from nose.tools import eq_, ok_\n'), ((267, 8, 268, 65), 'nose.tools.ok_', 'ok_', ({(267, 12, 268, 64): "({'term': {'device': 3}} in qs['query']['filtered']['filter']['bool']['must'])"}, {}), "({'term': {'device': 3}} in qs['query']['filtered']['filter']['bool'][\n    'must'])", False, 'from nose.tools import eq_, ok_\n'), ((270, 8, 271, 65), 'nose.tools.ok_', 'ok_', ({(270, 12, 271, 64): "({'term': {'device': 4}} in qs['query']['filtered']['filter']['bool']['must'])"}, {}), "({'term': {'device': 4}} in qs['query']['filtered']['filter']['bool'][\n    'must'])", False, 'from nose.tools import eq_, ok_\n'), ((280, 8, 281, 69), 'nose.tools.ok_', 'ok_', ({(280, 12, 281, 68): "({'term': {'region_exclusions': mkt.regions.RESTOFWORLD.id}} in qs['query']\n    ['filtered']['filter']['bool']['must_not'])"}, {}), "({'term': {'region_exclusions': mkt.regions.RESTOFWORLD.id}} in qs[\n    'query']['filtered']['filter']['bool']['must_not'])", False, 'from nose.tools import eq_, ok_\n'), ((286, 8, 287, 69), 'nose.tools.ok_', 'ok_', ({(286, 12, 287, 68): "({'term': {'region_exclusions': mkt.regions.BRA.id}} in qs['query'][\n    'filtered']['filter']['bool']['must_not'])"}, {}), "({'term': {'region_exclusions': mkt.regions.BRA.id}} in qs['query'][\n    'filtered']['filter']['bool']['must_not'])", False, 'from nose.tools import eq_, ok_\n'), ((308, 8, 309, 69), 'nose.tools.ok_', 'ok_', ({(308, 12, 309, 68): "({'term': {'features.has_sms': True}} in qs['query']['filtered']['filter'][\n    'bool']['must_not'])"}, {}), "({'term': {'features.has_sms': True}} in qs['query']['filtered'][\n    'filter']['bool']['must_not'])", False, 'from nose.tools import eq_, ok_\n'), ((320, 8, 321, 69), 'nose.tools.ok_', 'ok_', ({(320, 12, 321, 68): "({'term': {'features.has_sms': True}} in qs['query']['filtered']['filter'][\n    'bool']['must_not'])"}, {}), "({'term': {'features.has_sms': True}} in qs['query']['filtered'][\n    'filter']['bool']['must_not'])", False, 'from nose.tools import eq_, ok_\n'), ((322, 8, 323, 69), 'nose.tools.ok_', 'ok_', ({(322, 12, 323, 68): "({'term': {'features.has_apps': True}} in qs['query']['filtered']['filter']\n    ['bool']['must_not'])"}, {}), "({'term': {'features.has_apps': True}} in qs['query']['filtered'][\n    'filter']['bool']['must_not'])", False, 'from nose.tools import eq_, ok_\n'), ((331, 33, 331, 70), 'mkt.search.filters.SortingFilter.DEFAULT_SORTING.items', 'SortingFilter.DEFAULT_SORTING.items', ({}, {}), '()', False, 'from mkt.search.filters import DeviceTypeFilter, ProfileFilter, PublicAppsFilter, PublicSearchFormFilter, RegionFilter, SearchQueryFilter, SortingFilter, ValidAppsFilter\n'), ((340, 8, 340, 65), 'nose.tools.ok_', 'ok_', ({(340, 12, 340, 64): "({'bayesian_rating': {'order': 'desc'}} in qs['sort'])"}, {}), "({'bayesian_rating': {'order': 'desc'}} in qs['sort'])", False, 'from nose.tools import eq_, ok_\n'), ((341, 8, 341, 57), 'nose.tools.ok_', 'ok_', ({(341, 12, 341, 56): "({'created': {'order': 'desc'}} in qs['sort'])"}, {}), "({'created': {'order': 'desc'}} in qs['sort'])", False, 'from nose.tools import eq_, ok_\n'), ((349, 8, 350, 68), 'nose.tools.ok_', 'ok_', ({(349, 12, 350, 67): "({('popularity_%s' % mkt.regions.BRA.id): {'order': 'desc'}} in qs['sort'])"}, {}), "({('popularity_%s' % mkt.regions.BRA.id): {'order': 'desc'}} in qs['sort'])", False, 'from nose.tools import eq_, ok_\n'), ((355, 8, 356, 68), 'nose.tools.ok_', 'ok_', ({(355, 12, 356, 67): "({('popularity_%s' % mkt.regions.BRA.id): {'order': 'desc'}} in qs['sort'])"}, {}), "({('popularity_%s' % mkt.regions.BRA.id): {'order': 'desc'}} in qs['sort'])", False, 'from nose.tools import eq_, ok_\n'), ((361, 8, 362, 26), 'nose.tools.ok_', 'ok_', ({(361, 12, 362, 25): "({('trending_%s' % mkt.regions.BRA.id): {'order': 'desc'}} in qs['sort'])"}, {}), "({('trending_%s' % mkt.regions.BRA.id): {'order': 'desc'}} in qs['sort'])", False, 'from nose.tools import eq_, ok_\n'), ((377, 8, 377, 63), 'nose.tools.ok_', 'ok_', ({(377, 12, 377, 62): "qs['query']['filtered']['query']['function_score']"}, {}), "(qs['query']['filtered']['query']['function_score'])", False, 'from nose.tools import eq_, ok_\n'), ((378, 8, 378, 46), 'nose.tools.ok_', 'ok_', ({(378, 12, 378, 45): "qs['query']['filtered']['filter']"}, {}), "(qs['query']['filtered']['filter'])", False, 'from nose.tools import eq_, ok_\n'), ((381, 8, 381, 55), 'nose.tools.ok_', 'ok_', ({(381, 12, 381, 54): "({'terms': {'category': ['games']}} in must)"}, {}), "({'terms': {'category': ['games']}} in must)", False, 'from nose.tools import eq_, ok_\n'), ((382, 8, 382, 44), 'nose.tools.ok_', 'ok_', ({(382, 12, 382, 43): "({'term': {'status': 4}} in must)"}, {}), "({'term': {'status': 4}} in must)", False, 'from nose.tools import eq_, ok_\n'), ((383, 8, 383, 53), 'nose.tools.ok_', 'ok_', ({(383, 12, 383, 52): "({'term': {'is_disabled': False}} in must)"}, {}), "({'term': {'is_disabled': False}} in must)", False, 'from nose.tools import eq_, ok_\n'), ((385, 8, 385, 58), 'nose.tools.ok_', 'ok_', ({(385, 12, 385, 57): "({'trending': {'order': 'desc'}} in qs['sort'])"}, {}), "({'trending': {'order': 'desc'}} in qs['sort'])", False, 'from nose.tools import eq_, ok_\n'), ((388, 8, 389, 52), 'nose.tools.ok_', 'ok_', ({(388, 12, 389, 51): "({'field_value_factor': {'field': 'boost'}} in query['function_score'][\n    'functions'])"}, {}), "({'field_value_factor': {'field': 'boost'}} in query['function_score'][\n    'functions'])", False, 'from nose.tools import eq_, ok_\n'), ((390, 8, 391, 66), 'nose.tools.ok_', 'ok_', ({(390, 12, 391, 65): "({'match': {'name_english': {'boost': 2.5, 'query': u'test'}}} in query[\n    'function_score']['query']['bool']['should'])"}, {}), "({'match': {'name_english': {'boost': 2.5, 'query': u'test'}}} in query[\n    'function_score']['query']['bool']['should'])", False, 'from nose.tools import eq_, ok_\n'), ((84, 12, 86, 26), 'nose.tools.ok_', 'ok_', ({(84, 16, 86, 25): "({'match': {'name_polish': {'query': u'próba', 'boost': 2.5}}} in should)"}, {}), "({'match': {'name_polish': {'query': u'próba', 'boost': 2.5}}} in should)", False, 'from nose.tools import eq_, ok_\n'), ((87, 12, 91, 26), 'nose.tools.ok_', 'ok_', ({(87, 16, 91, 25): "({'match': {'description_polish': {'query': u'próba', 'boost': 0.6,\n    'analyzer': 'polish', 'type': 'phrase'}}} in should)"}, {}), "({'match': {'description_polish': {'query': u'próba', 'boost': 0.6,\n    'analyzer': 'polish', 'type': 'phrase'}}} in should)", False, 'from nose.tools import eq_, ok_\n'), ((185, 19, 185, 54), 'mkt.ADDON_PREMIUM_API_LOOKUP.get', 'mkt.ADDON_PREMIUM_API_LOOKUP.get', ({(185, 52, 185, 53): 'p'}, {}), '(p)', False, 'import mkt\n'), ((297, 44, 297, 60), 'mkt.constants.features.FeatureProfile', 'FeatureProfile', ({}, {}), '()', False, 'from mkt.constants.features import FeatureProfile\n'), ((28, 19, 28, 35), 'django.test.client.RequestFactory', 'RequestFactory', ({}, {}), '()', False, 'from django.test.client import RequestFactory\n'), ((180, 43, 180, 57), 'json.dumps', 'json.dumps', ({(180, 54, 180, 56): 'qs'}, {}), '(qs)', False, 'import json\n'), ((297, 18, 297, 34), 'mkt.constants.features.FeatureProfile', 'FeatureProfile', ({}, {}), '()', False, 'from mkt.constants.features import FeatureProfile\n'), ((334, 16, 334, 71), 'nose.tools.ok_', 'ok_', ({(334, 20, 334, 66): "({es_sort[1:]: {'order': 'desc'}} in qs['sort'])", (334, 68, 334, 70): 'qs'}, {}), "({es_sort[1:]: {'order': 'desc'}} in qs['sort'], qs)", False, 'from nose.tools import eq_, ok_\n'), ((336, 16, 336, 46), 'nose.tools.eq_', 'eq_', ({(336, 20, 336, 29): '[es_sort]', (336, 31, 336, 41): "qs['sort']", (336, 43, 336, 45): 'qs'}, {}), "([es_sort], qs['sort'], qs)", False, 'from nose.tools import eq_, ok_\n'), ((345, 14, 345, 30), 'django.test.client.RequestFactory', 'RequestFactory', ({}, {}), '()', False, 'from django.test.client import RequestFactory\n'), ((352, 14, 352, 30), 'django.test.client.RequestFactory', 'RequestFactory', ({}, {}), '()', False, 'from django.test.client import RequestFactory\n'), ((358, 14, 358, 30), 'django.test.client.RequestFactory', 'RequestFactory', ({}, {}), '()', False, 'from django.test.client import RequestFactory\n'), ((33, 21, 33, 37), 'django.test.client.RequestFactory', 'RequestFactory', ({}, {}), '()', False, 'from django.test.client import RequestFactory\n')]