def create_basic_filter(self, countries=None):
     if countries:
         country_objs = [CountryFactory(code=code) for code in countries]
     else:
         country_objs = [CountryFactory()]
     return filters.CountryFilter.create(
         countries=[c.code for c in country_objs])
Exemplo n.º 2
0
    def test_filter_by_country_one(self):
        country1 = CountryFactory()
        country2 = CountryFactory()
        recipe = RecipeFactory(countries=[country1])
        client1 = ClientFactory(country=country1.code)
        client2 = ClientFactory(country=country2.code)

        assert recipe.matches(client1)
        assert not recipe.matches(client2)
Exemplo n.º 3
0
    def test_update_recipe_country(self, api_client):
        c1 = CountryFactory(code='US')
        c2 = CountryFactory(code='CA')
        r = RecipeFactory(countries=[c1])

        res = api_client.patch(f'/api/v1/recipe/{r.pk}/',
                               {'countries': ['CA']})
        assert res.status_code == 200

        r.refresh_from_db()
        assert list(r.countries.all()) == [c2]
Exemplo n.º 4
0
    def test_list_filter_countries(self, api_client):
        r1 = RecipeFactory(countries=[CountryFactory(code='US')])
        r2 = RecipeFactory(countries=[CountryFactory(code='CA')])

        res = api_client.get('/api/v1/recipe/?countries=US')
        assert res.status_code == 200
        assert len(res.data) == 1
        assert res.data[0]['id'] == r1.id

        res = api_client.get('/api/v1/recipe/?countries=US,CA')
        assert res.status_code == 200
        assert len(res.data) == 2
        for recipe in res.data:
            assert recipe['id'] in [r1.id, r2.id]
Exemplo n.º 5
0
    def test_it_works(self, rf):
        channel = ChannelFactory()
        country = CountryFactory()
        locale = LocaleFactory()
        recipe = RecipeFactory(arguments={'foo': 'bar'},
                               channels=[channel],
                               countries=[country],
                               locales=[locale])
        action = recipe.action
        serializer = RecipeSerializer(recipe, context={'request': rf.get('/')})

        assert serializer.data == {
            'name': recipe.name,
            'id': recipe.id,
            'last_updated': Whatever(),
            'enabled': recipe.enabled,
            'extra_filter_expression': recipe.extra_filter_expression,
            'filter_expression': recipe.filter_expression,
            'revision_id': recipe.revision_id,
            'action': action.name,
            'arguments': {
                'foo': 'bar',
            },
            'channels': [channel.slug],
            'countries': [country.code],
            'locales': [locale.code]
        }
Exemplo n.º 6
0
 def test_canonical_json(self):
     recipe = RecipeFactory(
         action=ActionFactory(name='action'),
         arguments_json='{"foo": 1, "bar": 2}',
         channels=[ChannelFactory(slug='beta')],
         countries=[CountryFactory(code='CA')],
         enabled=False,
         extra_filter_expression='2 + 2 == 4',
         locales=[LocaleFactory(code='en-US')],
         name='canonical',
     )
     # Yes, this is really ugly, but we really do need to compare an exact
     # byte sequence, since this is used for hashing and signing
     filter_expression = (
         "(normandy.locale in ['en-US']) && (normandy.country in ['CA']) && "
         "(normandy.channel in ['beta']) && (2 + 2 == 4)")
     expected = ('{'
                 '"action":"action",'
                 '"arguments":{"bar":2,"foo":1},'
                 '"enabled":false,'
                 '"filter_expression":"%(filter_expression)s",'
                 '"id":%(id)s,'
                 '"is_approved":false,'
                 '"last_updated":"%(last_updated)s",'
                 '"name":"canonical",'
                 '"revision_id":"%(revision_id)s"'
                 '}') % {
                     'id': recipe.id,
                     'revision_id': recipe.revision_id,
                     'last_updated':
                     recipe.last_updated.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
                     'filter_expression': filter_expression
                 }
     expected = expected.encode()
     assert recipe.canonical_json() == expected
Exemplo n.º 7
0
    def test_classify_empty_time(self, admin_client):
        """
        If the request_time isn't provided, default to the current time.
        """
        locale, other_locale = LocaleFactory.create_batch(2)
        country = CountryFactory()
        release_channel = ReleaseChannelFactory()

        matching_recipe = RecipeFactory(
            enabled=True,
            locales=[locale],
            countries=[country],
            release_channels=[release_channel],
            start_time=aware_datetime(2016, 1, 1),
            end_time=aware_datetime(2016, 1, 3),
        )
        RecipeFactory(locales=[other_locale])  # Non-matching

        with patch('normandy.classifier.middleware.timezone') as timezone:
            timezone.now.return_value = aware_datetime(2016, 1, 2)
            response = admin_client.get(
                '/admin/classifier_preview', {
                    'locale': locale.code,
                    'release_channel': release_channel.slug,
                    'country': country.code,
                    'request_time_0': '',
                    'request_time_1': '',
                })

        assert response.status_code == 200
        assert list(response.context['bundle']) == [matching_recipe]
Exemplo n.º 8
0
    def test_classify(self, admin_client):
        locale, other_locale = LocaleFactory.create_batch(2)
        country = CountryFactory()
        release_channel = ReleaseChannelFactory()

        matching_recipe = RecipeFactory(
            enabled=True,
            locales=[locale],
            countries=[country],
            release_channels=[release_channel],
            start_time=aware_datetime(2016, 1, 1),
            end_time=aware_datetime(2016, 1, 3),
        )
        RecipeFactory(locales=[other_locale])  # Non-matching

        response = admin_client.get(
            '/admin/classifier_preview', {
                'locale': locale.code,
                'release_channel': release_channel.slug,
                'country': country.code,
                'request_time_0': '2016-01-02',
                'request_time_1': '00:00:00',
            })

        assert response.status_code == 200
        assert list(response.context['bundle']) == [matching_recipe]
Exemplo n.º 9
0
    def test_recipe_revise_countries(self):
        c1 = CountryFactory(code='CA')
        recipe = RecipeFactory(countries=[c1])

        c2 = CountryFactory(code='US')
        recipe.revise(countries=[c2])
        assert recipe.countries.count() == 1
        assert list(recipe.countries.all()) == [c2]

        recipe.revise(countries=[c1, c2])
        countries = list(recipe.countries.all())
        assert recipe.countries.count() == 2
        assert c1 in countries
        assert c2 in countries

        recipe.revise(countries=[])
        assert recipe.countries.count() == 0
Exemplo n.º 10
0
    def test_validation_with_valid_data(self):
        mockAction = ActionFactory(name='show-heartbeat',
                                   arguments_schema=ARGUMENTS_SCHEMA)

        channel = ChannelFactory(slug='release')
        country = CountryFactory(code='CA')
        locale = LocaleFactory(code='en-US')

        serializer = RecipeSerializer(
            data={
                'name': 'bar',
                'enabled': True,
                'extra_filter_expression': '[]',
                'action': 'show-heartbeat',
                'channels': ['release'],
                'countries': ['CA'],
                'locales': ['en-US'],
                'arguments': {
                    'surveyId':
                    'lorem-ipsum-dolor',
                    'surveys': [{
                        'title': 'adipscing',
                        'weight': 1
                    }, {
                        'title': 'consequetar',
                        'weight': 1
                    }]
                }
            })

        assert serializer.is_valid()
        assert serializer.validated_data == {
            'name': 'bar',
            'enabled': True,
            'extra_filter_expression': '[]',
            'action': mockAction,
            'arguments': {
                'surveyId':
                'lorem-ipsum-dolor',
                'surveys': [{
                    'title': 'adipscing',
                    'weight': 1
                }, {
                    'title': 'consequetar',
                    'weight': 1
                }]
            },
            'channels': [channel],
            'countries': [country],
            'locales': [locale],
        }
        assert serializer.errors == {}
Exemplo n.º 11
0
    def test_filter_expression(self):
        channel1 = ChannelFactory(slug='beta', name='Beta')
        channel2 = ChannelFactory(slug='release', name='Release')
        country1 = CountryFactory(code='US', name='USA')
        country2 = CountryFactory(code='CA', name='Canada')
        locale1 = LocaleFactory(code='en-US', name='English (US)')
        locale2 = LocaleFactory(code='fr-CA', name='French (CA)')

        r = RecipeFactory()
        assert r.filter_expression == ''

        r = RecipeFactory(channels=[channel1])
        assert r.filter_expression == "normandy.channel in ['beta']"

        r.revise(channels=[channel1, channel2])
        assert r.filter_expression == "normandy.channel in ['beta', 'release']"

        r = RecipeFactory(countries=[country1])
        assert r.filter_expression == "normandy.country in ['US']"

        r.revise(countries=[country1, country2])
        assert r.filter_expression == "normandy.country in ['CA', 'US']"

        r = RecipeFactory(locales=[locale1])
        assert r.filter_expression == "normandy.locale in ['en-US']"

        r.revise(locales=[locale1, locale2])
        assert r.filter_expression == "normandy.locale in ['en-US', 'fr-CA']"

        r = RecipeFactory(extra_filter_expression='2 + 2 == 4')
        assert r.filter_expression == '2 + 2 == 4'

        r.revise(channels=[channel1], countries=[country1], locales=[locale1])
        assert r.filter_expression == ("(normandy.locale in ['en-US']) && "
                                       "(normandy.country in ['US']) && "
                                       "(normandy.channel in ['beta']) && "
                                       "(2 + 2 == 4)")
Exemplo n.º 12
0
    def test_classify_no_sample(self, admin_client):
        """The classify view should ignore sampling."""
        locale = LocaleFactory()
        country = CountryFactory()
        release_channel = ReleaseChannelFactory()

        recipe = RecipeFactory(sample_rate=0)
        response = admin_client.get(
            '/admin/classifier_preview', {
                'locale': locale.code,
                'release_channel': release_channel.slug,
                'country': country.code
            })

        assert response.status_code == 200
        assert not recipe.matches(response.context['client'])
        assert list(response.context['bundle']) == [recipe]
Exemplo n.º 13
0
    def test_it_works(self, rf):
        channel = ChannelFactory()
        country = CountryFactory()
        locale = LocaleFactory()
        recipe = RecipeFactory(arguments={'foo': 'bar'},
                               channels=[channel],
                               countries=[country],
                               locales=[locale])
        approval = ApprovalRequestFactory(revision=recipe.latest_revision)
        action = recipe.action
        serializer = RecipeSerializer(recipe, context={'request': rf.get('/')})

        assert serializer.data == {
            'name': recipe.name,
            'id': recipe.id,
            'last_updated': Whatever(),
            'enabled': recipe.enabled,
            'extra_filter_expression': recipe.extra_filter_expression,
            'filter_expression': recipe.filter_expression,
            'action': {
                'arguments_schema': {},
                'id': action.id,
                'implementation_url': Whatever(),
                'name': action.name,
            },
            'arguments': {
                'foo': 'bar',
            },
            'channels': [channel.slug],
            'countries': [country.code],
            'locales': [locale.code],
            'is_approved': False,
            'latest_revision':
            RecipeRevisionSerializer(recipe.latest_revision).data,
            'approved_revision': None,
            'approval_request': {
                'id': approval.id,
                'created': Whatever(),
                'creator': Whatever(),
                'approved': None,
                'approver': None,
                'comment': None,
            },
        }