Skip to content

Commit

Permalink
test fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
chrstinalin committed Nov 24, 2024
1 parent 41e5862 commit b8b3a9a
Show file tree
Hide file tree
Showing 20 changed files with 229 additions and 116 deletions.
17 changes: 11 additions & 6 deletions src/olympia/addons/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
)
from olympia.constants.browsers import BROWSERS
from olympia.constants.categories import CATEGORIES_BY_ID
from olympia.constants.promoted import RECOMMENDED
from olympia.constants.reviewers import REPUTATION_CHOICES
from olympia.files.models import File
from olympia.files.utils import extract_translations, resolve_i18n_message
Expand Down Expand Up @@ -1571,9 +1570,9 @@ def promoted_groups(self, *, currently_approved=True):
return None
is_promoted = not currently_approved or promoted.approved_applications
return promoted if is_promoted else None

def check_permission(self, permission, *, truthy=True, currently_approved=True):
""" Does the addon have permissions?
"""Does the addon have permissions?
`currently_approved=True` means only returns True if
self.current_version is approved for the current promotion & apps.
Expand All @@ -1585,7 +1584,11 @@ def check_permission(self, permission, *, truthy=True, currently_approved=True):
except PromotedAddon.DoesNotExist:
return False
is_promoted = not currently_approved or promoted.approved_applications
return promoted.check_permission(permission, truthy) if is_promoted else False
return (
promoted.check_permission(permission, self.current_version, truthy)
if is_promoted
else False
)

@cached_property
def promoted(self):
Expand All @@ -1596,7 +1599,7 @@ def promoted(self):
from olympia.promoted.models import PromotedTheme

if self._is_recommended_theme():
return PromotedTheme(addon=self, group_id=RECOMMENDED.id)
return PromotedTheme(addon=self)
return None

@cached_property
Expand All @@ -1622,7 +1625,9 @@ def can_be_compatible_with_all_fenix_versions(self):
versions (i.e. it's a recommended/line extension for Android)."""
return (
self.promoted
and self.promoted.check_permission('can_be_compatible_with_all_fenix_versions')
and self.promoted.check_permission(
'can_be_compatible_with_all_fenix_versions'
)
and amo.ANDROID in self.promoted.approved_applications
)

Expand Down
2 changes: 2 additions & 0 deletions src/olympia/addons/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -964,10 +964,12 @@ def validate_user_id(self, value):
class PromotedAddonGroupSerializer(AMOModelSerializer):
GROUP_CHOICES = [(group.id, group.api_name) for group in PROMOTED_GROUPS]
group_id = serializers.ChoiceField(choices=GROUP_CHOICES)

class Meta:
model = PromotedAddonGroup
fields = ('group_id',)


class PromotedAddonSerializer(AMOModelSerializer):
apps = serializers.SerializerMethodField()
categories = PromotedAddonGroupSerializer(many=True, source='promoted_groups')
Expand Down
6 changes: 4 additions & 2 deletions src/olympia/addons/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
from olympia.bandwagon.models import Collection
from olympia.blocklist.models import Block, BlocklistSubmission
from olympia.constants.categories import CATEGORIES
from olympia.constants.promoted import LINE, NOT_PROMOTED, RECOMMENDED, SPOTLIGHT
from olympia.constants.promoted import LINE, RECOMMENDED, SPOTLIGHT
from olympia.devhub.models import RssKey
from olympia.files.models import File
from olympia.files.tests.test_models import UploadMixin
Expand Down Expand Up @@ -1795,7 +1795,9 @@ def test_promoted(self):

# If the group changes the approval for the current version isn't
# valid.
PromotedAddonGroup.objects.filter(promoted_addon=promoted, group_id=LINE.id).update(group_id=SPOTLIGHT.id)
PromotedAddonGroup.objects.filter(
promoted_addon=promoted, group_id=LINE.id
).update(group_id=SPOTLIGHT.id)
del addon.promoted
assert addon.promoted is None

Expand Down
10 changes: 5 additions & 5 deletions src/olympia/amo/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
from django.utils.encoding import force_str
from django.utils.html import escape

from olympia.constants.promoted import NOT_PROMOTED
import pytest
from rest_framework.reverse import reverse as drf_reverse
from rest_framework.settings import api_settings
Expand All @@ -56,6 +55,7 @@
from olympia.bandwagon.models import Collection
from olympia.blocklist.models import Block, BlockType, BlockVersion
from olympia.constants.categories import CATEGORIES
from olympia.constants.promoted import NOT_PROMOTED
from olympia.files.models import File
from olympia.promoted.models import (
PromotedAddon,
Expand Down Expand Up @@ -586,9 +586,7 @@ def change_channel_for_addon(self, addon, listed):

@classmethod
def make_addon_promoted(cls, addon, group, approve_version=False):
obj, created = PromotedAddon.objects.update_or_create(
addon=addon
)
obj, created = PromotedAddon.objects.update_or_create(addon=addon)
if group == NOT_PROMOTED:
PromotedAddonGroup.objects.filter(promoted_addon=obj).delete()
else:
Expand Down Expand Up @@ -734,7 +732,9 @@ def addon_factory(status=amo.STATUS_APPROVED, version_kw=None, file_kw=None, **k
# Save 2.
if promoted_group:
promoted = PromotedAddon.objects.create(addon=addon)
PromotedAddonGroup.objects.create(promoted_addon=promoted, group_id=promoted_group.id)
PromotedAddonGroup.objects.create(
promoted_addon=promoted, group_id=promoted_group.id
)
if 'promotion_approved' not in version_kw:
version_kw['promotion_approved'] = True

Expand Down
5 changes: 2 additions & 3 deletions src/olympia/devhub/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -867,9 +867,8 @@ def __init__(self, *args, **kwargs):

# On Android, disable Fenix-pre GA version range unless the add-on is
# recommended or line.
if (
app == amo.ANDROID.id
and not addon.check_permission('can_be_compatible_with_all_fenix_versions')
if app == amo.ANDROID.id and not addon.check_permission(
'can_be_compatible_with_all_fenix_versions'
):
self.fields[
'min'
Expand Down
11 changes: 9 additions & 2 deletions src/olympia/devhub/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1585,8 +1585,15 @@ def _submit_upload(
# If we're not showing the generic submit notification warning, show
# one specific to pre review if the developer would be affected because
# of its promoted group.
if (channel == amo.CHANNEL_LISTED and addon.check_permission('listed_pre_review', currently_approved=False) or (
channel == amo.CHANNEL_UNLISTED and addon.check_permission('unlisted_pre_review', currently_approved=False))
if (
channel == amo.CHANNEL_LISTED
and addon.check_permission('listed_pre_review', currently_approved=False)
or (
channel == amo.CHANNEL_UNLISTED
and addon.check_permission(
'unlisted_pre_review', currently_approved=False
)
)
):
submit_notification_warning = get_config(
'submit_notification_warning_pre_review'
Expand Down
34 changes: 18 additions & 16 deletions src/olympia/hero/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ def test_gradiant(self):
assert ph.gradient == {'start': 'color-ink-80', 'end': 'color-pink-70'}

def test_clean_requires_approved_can_primary_hero_group(self):
promoted = PromotedAddon.objects.create(
addon=addon_factory()
)
PromotedAddonGroup.objects.create(promoted_addon=promoted, group_id=RECOMMENDED.id)
promoted = PromotedAddon.objects.create(addon=addon_factory())
PromotedAddonGroup.objects.create(
promoted_addon=promoted, group_id=RECOMMENDED.id
)
ph = PrimaryHero.objects.create(
promoted_addon=promoted,
gradient_color='#C60184',
Expand All @@ -64,7 +64,9 @@ def test_clean_requires_approved_can_primary_hero_group(self):
ph.clean() # it raises if there's an error

# change to a different group
PromotedAddonGroup.objects.filter(promoted_addon=ph.promoted_addon).update(group_id=STRATEGIC.id)
PromotedAddonGroup.objects.filter(promoted_addon=ph.promoted_addon).update(
group_id=STRATEGIC.id
)
ph.promoted_addon.approve_for_version(ph.promoted_addon.addon.current_version)
ph.reload()
ph.enabled = True
Expand All @@ -78,7 +80,9 @@ def test_clean_requires_approved_can_primary_hero_group(self):
]

# change to a different group that *can* be added as a primary hero
PromotedAddonGroup.objects.filter(promoted_addon=ph.promoted_addon).update(group_id=SPOTLIGHT.id)
PromotedAddonGroup.objects.filter(promoted_addon=ph.promoted_addon).update(
group_id=SPOTLIGHT.id
)
ph.promoted_addon.approve_for_version(ph.promoted_addon.addon.current_version)
ph.reload()
ph.enabled = True
Expand All @@ -104,13 +108,11 @@ def test_clean_external_requires_homepage(self):

def test_clean_gradient_and_image(self):
# Currently, gradient is required and image isn't.
promoted = PromotedAddon.objects.create(
addon=addon_factory()
)
PromotedAddonGroup.objects.create(promoted_addon=promoted, group_id=RECOMMENDED.id)
ph = PrimaryHero.objects.create(
promoted_addon=promoted
promoted = PromotedAddon.objects.create(addon=addon_factory())
PromotedAddonGroup.objects.create(
promoted_addon=promoted, group_id=RECOMMENDED.id
)
ph = PrimaryHero.objects.create(promoted_addon=promoted)
ph.promoted_addon.approve_for_version(ph.promoted_addon.addon.current_version)
ph.reload()
assert not ph.enabled
Expand All @@ -131,10 +133,10 @@ def test_clean_gradient_and_image(self):
ph.clean() # it raises if there's an error

def test_clean_only_enabled(self):
promoted = PromotedAddon.objects.create(
addon=addon_factory()
)
PromotedAddonGroup.objects.create(promoted_addon=promoted, group_id=RECOMMENDED.id)
promoted = PromotedAddon.objects.create(addon=addon_factory())
PromotedAddonGroup.objects.create(
promoted_addon=promoted, group_id=RECOMMENDED.id
)
hero = PrimaryHero.objects.create(
promoted_addon=promoted,
gradient_color='#C60184',
Expand Down
2 changes: 2 additions & 0 deletions src/olympia/promoted/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def get_queryset(self, request):
)
return qs


class PromotedAddonGroupInline(admin.TabularInline):
model = PromotedAddonGroup
fields = ('group_id',)
Expand All @@ -75,6 +76,7 @@ def get_queryset(self, request):
qs = qs.filter(promoted_addon=self.instance)
return qs


class PromotedAddonAdmin(AMOModelAdmin):
list_display = (
'addon__name',
Expand Down
61 changes: 43 additions & 18 deletions src/olympia/promoted/models.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from django.core.exceptions import ValidationError
from django.db import models
from django.dispatch import receiver

from olympia.addons.models import Addon
from olympia.amo.models import ManagerBase, ModelBase
from django.core.exceptions import ValidationError
from olympia.constants.applications import APP_IDS, APP_USAGE, APPS_CHOICES
from olympia.constants.promoted import (
NOT_PROMOTED,
Expand All @@ -13,10 +13,12 @@
from olympia.reviewers.models import NeedsHumanReview
from olympia.versions.models import Version


class PromotedAddonManager(ManagerBase):
def get_queryset(self):
return super().get_queryset().prefetch_related('promoted_groups')


class PromotedAddon(ModelBase):
APPLICATION_CHOICES = ((None, 'All Applications'),) + APPS_CHOICES
addon = models.OneToOneField(
Expand All @@ -41,7 +43,7 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def __str__(self):
return f"{self.name} - {self.addon}"
return f'{self.name} - {self.addon}'

@property
def badged(self):
Expand All @@ -57,34 +59,45 @@ def groups(self):
Return the set of groups.
"""
return self.promoted_groups.all()

@property
def group_ids(self):
"""
Return the list of group ids.
"""
return list(self.groups.values_list('group_id', flat=True))

@property
def not_promoted(self):
"""
Return whether the PromotedAddon is not promoted.
"""
return not self.groups

def in_group(self, group):
"""
Return whether the PromotedAddon is part of the given group.
"""
return group.id in self.group_ids
def check_permission(self, permission, truthy=True):

def check_permission(self, permission, version=None, truthy=True):
"""
Whether any promoted group the add-on is apart of has the associated permission.
When truthy is passed, checks for that boolean value.
Whether any promoted group the add-on is apart of has the associated permission.
If version is passed, only returns True if the associated group is approved
for the given version.
If truthy is passed, checks for that boolean value.
"""
return any(group.check_permission(permission, truthy) for group in self.groups)

return any(
group.check_permission(permission, truthy)
for group in self.groups
if not version
or PromotedApproval.objects.filter(
version=version, group_id=group.group_id
).exists()
)

@property
def all_applications(self):
application = APP_IDS.get(self.application_id)
Expand Down Expand Up @@ -117,8 +130,9 @@ def _get_application_id_from_applications(self, apps):
def approve_for_version(self, version, group_id=None):
"""Create PromotedApproval for current applications in the given
promoted group.
If group_id is passed, only approve group_id. otherwise, approve all in self.groups.
If group_id is passed, only approve group_id.
Otherwise, approve all in self.groups.
"""
for app in self.all_applications:
group_ids = [group_id] if group_id else self.group_ids
Expand Down Expand Up @@ -154,11 +168,13 @@ def save(self, *args, **kwargs):
due_date=due_date,
reason=NeedsHumanReview.REASONS.ADDED_TO_PROMOTED_GROUP,
)


class PromotedAddonGroup(ModelBase):
GROUP_CHOICES = [(group.id, group.name) for group in PROMOTED_GROUPS]
promoted_addon = models.ForeignKey(PromotedAddon, on_delete=models.CASCADE, related_name='promoted_groups')
promoted_addon = models.ForeignKey(
PromotedAddon, on_delete=models.CASCADE, related_name='promoted_groups'
)
group_id = models.SmallIntegerField(
choices=GROUP_CHOICES,
verbose_name='Group',
Expand All @@ -174,16 +190,25 @@ def badged(self):
def check_permission(self, permission, truthy=True):
result = getattr(PROMOTED_GROUPS_BY_ID[self.group_id], permission, False)
return result if truthy else not result

class Meta:
constraints = [
models.UniqueConstraint(
fields=['group_id', 'promoted_addon'], name='promoted_addon_group_id'
)
]

def clean(self):
addon_groups = PromotedAddonGroup.objects.filter(promoted_addon=self.promoted_addon)
if addon_groups.filter(group_id=NOT_PROMOTED.id).exists() and addon_groups.len() > 1:
raise ValidationError('User cannot be promoted and not promoted at the same time.')
addon_groups = PromotedAddonGroup.objects.filter(
promoted_addon=self.promoted_addon
)
if (
addon_groups.filter(group_id=NOT_PROMOTED.id).exists()
and addon_groups.len() > 1
):
raise ValidationError(
'User cannot be promoted and not promoted at the same time.'
)
super().clean()


Expand Down
1 change: 1 addition & 0 deletions src/olympia/promoted/tasks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.db.models import Q

import olympia.core.logger
from olympia import amo
from olympia.addons.models import Addon
Expand Down
Loading

0 comments on commit b8b3a9a

Please sign in to comment.