implement admin reviewer unreject versions action (#19728)

* implement admin reviewer unreject versions action

* don't subscribe the reviewer to the review

* move status setting code to Addon model
This commit is contained in:
Andrew Williamson 2022-10-11 12:47:08 +01:00 коммит произвёл GitHub
Родитель 48667c56f2
Коммит 3fcfd57396
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 547 добавлений и 171 удалений

Просмотреть файл

@ -1235,6 +1235,22 @@ class Addon(OnChangeMixin, ModelBase):
self.update_version(ignore=ignore_version)
def update_nominated_status(self, user):
# Update the addon status to nominated if there are versions awaiting review
if (
self.status == amo.STATUS_NULL
and self.versions.filter(
file__status=amo.STATUS_AWAITING_REVIEW, channel=amo.CHANNEL_LISTED
).exists()
):
log.info(
'Changing add-on status [%s]: %s => %s (%s).'
% (self.id, self.status, amo.STATUS_NOMINATED, 'unrejecting versions')
)
self.update(status=amo.STATUS_NOMINATED)
activity.log_create(amo.LOG.CHANGE_STATUS, self, self.status, user=user)
self.update_version()
@staticmethod
def attach_related_versions(addons, addon_dict=None):
if addon_dict is None:

Просмотреть файл

@ -2104,6 +2104,29 @@ class TestUpdateStatus(TestCase):
amo.STATUS_NULL
) # No listed versions so now NULL
def test_update_nominated_status(self):
addon = addon_factory(
status=amo.STATUS_NULL, file_kw={'status': amo.STATUS_DISABLED}
)
first_version = addon.versions.last()
version_factory(addon=addon, file_kw={'status': amo.STATUS_APPROVED})
user = user_factory()
# if add-on has no files that are AWAITING_REVIEW, the status shouldn't change
addon.update_nominated_status(user)
assert addon.reload().status == amo.STATUS_NULL
addon.update(status=amo.STATUS_DISABLED)
first_version.file.update(status=amo.STATUS_AWAITING_REVIEW)
# and neither should the status change if the addon status isn't NULL
addon.update_nominated_status(user)
addon.refresh_from_db() # this clears the fk relations too
assert addon.status == amo.STATUS_DISABLED
addon.update(status=amo.STATUS_NULL)
# success case - has version that is awaiting review and incomplete addon status
addon.update_nominated_status(user)
assert addon.reload().status == amo.STATUS_NOMINATED
class TestGetVersion(TestCase):
fixtures = [

Просмотреть файл

@ -844,6 +844,17 @@ class RESTRICTED(_LOG):
format = _('{user} restricted.')
class UNREJECT_VERSION(_LOG):
# takes add-on, version
id = 171
action_class = 'reject'
format = _('{addon} {version} unrejected.')
short = _('Unrejected')
keep = True
review_queue = True
reviewer_review_action = True
LOGS = [x for x in vars().values() if isclass(x) and issubclass(x, _LOG) and x != _LOG]
# Make sure there's no duplicate IDs.
assert len(LOGS) == len({log.id for log in LOGS})

Просмотреть файл

@ -112,11 +112,28 @@ class VersionsChoiceWidget(forms.SelectMultiple):
"""
actions_filters = {
amo.STATUS_APPROVED: ('confirm_multiple_versions', 'block_multiple_versions'),
amo.STATUS_AWAITING_REVIEW: (
'reject_multiple_versions',
'approve_multiple_versions',
),
amo.CHANNEL_UNLISTED: {
amo.STATUS_APPROVED: (
'block_multiple_versions',
'confirm_multiple_versions',
),
amo.STATUS_AWAITING_REVIEW: (
'approve_multiple_versions',
'reject_multiple_versions',
),
amo.STATUS_DISABLED: ('unreject_multiple_versions',),
},
amo.CHANNEL_LISTED: {
amo.STATUS_APPROVED: (
'block_multiple_versions',
'reject_multiple_versions',
),
amo.STATUS_AWAITING_REVIEW: (
'approve_multiple_versions',
'reject_multiple_versions',
),
amo.STATUS_DISABLED: ('unreject_multiple_versions',),
},
}
def create_option(self, *args, **kwargs):
@ -126,13 +143,12 @@ class VersionsChoiceWidget(forms.SelectMultiple):
obj = option['label']
status = obj.file.status if obj.file else None
versions_actions = getattr(self, 'versions_actions', None)
if versions_actions and obj.channel == amo.CHANNEL_UNLISTED:
# For unlisted, some actions should only apply to approved/pending
# versions, so we add our special `data-toggle` class and the
# right `data-value` depending on status.
if versions_actions:
# Add our special `data-toggle` class and the right `data-value` depending
# on status.
option['attrs']['class'] = 'data-toggle'
option['attrs']['data-value'] = '|'.join(
self.actions_filters.get(status, ()) + ('',)
self.actions_filters[obj.channel].get(status, ()) + ('',)
)
# Just in case, let's now force the label to be a string (it would be
# converted anyway, but it's probably safer that way).
@ -218,7 +234,7 @@ class ReviewForm(forms.Form):
if action:
if not action.get('comments', True):
self.fields['comments'].required = False
if action.get('versions', False):
if action.get('multiple_versions', False):
self.fields['versions'].required = True
if not action.get('requires_reasons', False):
self.fields['reasons'].required = False
@ -255,18 +271,19 @@ class ReviewForm(forms.Form):
# if the relevant actions are available, otherwise we don't really care
# about this field.
versions_actions = [
k for k in self.helper.actions if self.helper.actions[k].get('versions')
k
for k in self.helper.actions
if self.helper.actions[k].get('multiple_versions')
]
if versions_actions:
if self.helper.version:
channel = self.helper.version.channel
else:
channel = amo.CHANNEL_LISTED
statuses = (amo.STATUS_APPROVED, amo.STATUS_AWAITING_REVIEW)
self.fields['versions'].widget.versions_actions = versions_actions
self.fields['versions'].queryset = (
self.helper.addon.versions(manager='unfiltered_for_relations')
.filter(channel=channel, file__status__in=statuses)
.filter(channel=channel)
.no_transforms()
.select_related('file')
.select_related('autoapprovalsummary')

Просмотреть файл

@ -61,6 +61,16 @@ class TestReviewForm(TestCase):
)
assert list(actions.keys()) == ['reply', 'super', 'comment']
# If an admin reviewer we also show unreject_multiple_versions
self.grant_permission(self.request.user, 'Reviews:Admin')
actions = self.get_form().helper.get_actions()
assert list(actions.keys()) == [
'unreject_multiple_versions',
'reply',
'super',
'comment',
]
def test_actions_addon_status_deleted(self):
# If the add-on is deleted we only show reply, comment and
# super review.
@ -84,7 +94,18 @@ class TestReviewForm(TestCase):
'comment',
]
# The add-on is already disabled so we don't show
# If an admin reviewer we also show unreject_multiple_versions
self.grant_permission(self.request.user, 'Reviews:Admin')
actions = self.get_form().helper.get_actions()
assert list(actions.keys()) == [
'reject_multiple_versions',
'unreject_multiple_versions',
'reply',
'super',
'comment',
]
# The add-on is already disabled so we don't show unreject_multiple_versions or
# reject_multiple_versions, but reply/super/comment are still present.
actions = self.set_statuses_and_get_actions(
addon_status=amo.STATUS_DISABLED, file_status=amo.STATUS_DISABLED
@ -266,14 +287,24 @@ class TestReviewForm(TestCase):
assert form.fields['versions'].required is False
assert list(form.fields['versions'].queryset) == [self.addon.current_version]
def test_versions_queryset_contains_pending_files_for_listed(self):
def test_versions_queryset_contains_pending_files_for_listed(
self, select_data_value='reject_multiple_versions|'
):
# We hide some of the versions using JavaScript + some data attributes on each
# <option>.
# The queryset should contain both pending, rejected, and approved versions.
self.grant_permission(self.request.user, 'Addons:Review')
addon_factory() # Extra add-on, shouldn't be included.
version_factory(
pending_version = version_factory(
addon=self.addon,
channel=amo.CHANNEL_LISTED,
file_kw={'status': amo.STATUS_AWAITING_REVIEW},
)
rejected_version = version_factory(
addon=self.addon,
channel=amo.CHANNEL_LISTED,
file_kw={'status': amo.STATUS_DISABLED},
)
# auto-approve everything (including self.addon.current_version)
for version in Version.unfiltered.all():
AutoApprovalSummary.objects.create(
@ -285,35 +316,71 @@ class TestReviewForm(TestCase):
assert list(form.fields['versions'].queryset) == list(
self.addon.versions.all().order_by('pk')
)
assert form.fields['versions'].queryset.count() == 2
assert form.fields['versions'].queryset.count() == 3
content = str(form['versions'])
doc = pq(content)
# <select> should have 'data-toggle' class and data-value attribute to
# show/hide it depending on action in JavaScript.
select = doc('select')[0]
select.attrib.get('class') == 'data-toggle'
assert select.attrib.get('data-value') == 'reject_multiple_versions|'
assert select.attrib.get('data-value') == select_data_value
# <option>s shouldn't, because for listed review they will all be
# shown. They should still have a value attribute however.
options = doc('option')
assert len(options) == 2
for option in options:
assert option.attrib.get('class') is None
assert option.attrib.get('data-value') is None
assert option.attrib.get('value')
# <option>s should as well, and the value depends on which version:
# the approved one and the pending one should have different values.
assert len(doc('option')) == 3
option1 = doc('option[value="%s"]' % self.version.pk)[0]
assert option1.attrib.get('class') == 'data-toggle'
assert option1.attrib.get('data-value') == (
# That version is approved.
'block_multiple_versions|reject_multiple_versions|'
)
assert option1.attrib.get('value') == str(self.version.pk)
def test_versions_queryset_contains_pending_files_for_unlisted(self):
# We also return pending versions for unlisted, but hide some of the
# versions using JavaScript + some data attributes on each <option>.
# The queryset should contain both pending and approved versions.
option2 = doc('option[value="%s"]' % pending_version.pk)[0]
assert option2.attrib.get('class') == 'data-toggle'
assert option2.attrib.get('data-value') == (
# That version is pending.
'approve_multiple_versions|reject_multiple_versions|'
)
assert option2.attrib.get('value') == str(pending_version.pk)
option3 = doc('option[value="%s"]' % rejected_version.pk)[0]
assert option3.attrib.get('class') == 'data-toggle'
assert option3.attrib.get('data-value') == (
# That version is rejected.
'unreject_multiple_versions|'
)
assert option3.attrib.get('value') == str(rejected_version.pk)
def test_versions_queryset_contains_pending_files_for_listed_admin_reviewer(self):
self.grant_permission(self.request.user, 'Reviews:Admin')
self.test_versions_queryset_contains_pending_files_for_listed(
select_data_value='reject_multiple_versions|unreject_multiple_versions|'
)
def test_versions_queryset_contains_pending_files_for_unlisted(
self,
select_data_value=(
'approve_multiple_versions|reject_multiple_versions|'
'block_multiple_versions|confirm_multiple_versions|'
),
):
# We hide some of the versions using JavaScript + some data attributes on each
# <option>.
# The queryset should contain both pending, rejected, and approved versions.
addon_factory() # Extra add-on, shouldn't be included.
pending_version = version_factory(
addon=self.addon,
channel=amo.CHANNEL_UNLISTED,
file_kw={'status': amo.STATUS_AWAITING_REVIEW},
)
rejected_version = version_factory(
addon=self.addon,
channel=amo.CHANNEL_UNLISTED,
file_kw={'status': amo.STATUS_DISABLED},
)
self.version.update(channel=amo.CHANNEL_UNLISTED)
# auto-approve everything
for version in Version.unfiltered.all():
@ -334,27 +401,25 @@ class TestReviewForm(TestCase):
assert list(form.fields['versions'].queryset) == list(
self.addon.versions.all().order_by('pk')
)
assert form.fields['versions'].queryset.count() == 2
assert form.fields['versions'].queryset.count() == 3
content = str(form['versions'])
doc = pq(content)
# <select> should have 'data-toggle' class and data-value attribute to
# show/hide it depending on action in JavaScript.
select = doc('select')[0]
select.attrib.get('class') == 'data-toggle'
assert select.attrib.get('data-value') == (
'approve_multiple_versions|reject_multiple_versions|'
'block_multiple_versions|confirm_multiple_versions|'
)
assert select.attrib.get('data-value') == select_data_value
# <option>s should as well, and the value depends on which version:
# the approved one and the pending one should have different values.
assert len(doc('option')) == 2
assert len(doc('option')) == 3
option1 = doc('option[value="%s"]' % self.version.pk)[0]
assert option1.attrib.get('class') == 'data-toggle'
assert option1.attrib.get('data-value') == (
# That version is approved.
'confirm_multiple_versions|block_multiple_versions|'
'block_multiple_versions|confirm_multiple_versions|'
)
assert option1.attrib.get('value') == str(self.version.pk)
@ -362,10 +427,28 @@ class TestReviewForm(TestCase):
assert option2.attrib.get('class') == 'data-toggle'
assert option2.attrib.get('data-value') == (
# That version is pending.
'reject_multiple_versions|approve_multiple_versions|'
'approve_multiple_versions|reject_multiple_versions|'
)
assert option2.attrib.get('value') == str(pending_version.pk)
option3 = doc('option[value="%s"]' % rejected_version.pk)[0]
assert option3.attrib.get('class') == 'data-toggle'
assert option3.attrib.get('data-value') == (
# That version is rejected.
'unreject_multiple_versions|'
)
assert option3.attrib.get('value') == str(rejected_version.pk)
def test_versions_queryset_contains_pending_files_for_unlisted_admin_reviewer(self):
self.grant_permission(self.request.user, 'Reviews:Admin')
self.test_versions_queryset_contains_pending_files_for_unlisted(
select_data_value=(
'approve_multiple_versions|reject_multiple_versions|'
'unreject_multiple_versions|block_multiple_versions|'
'confirm_multiple_versions|'
)
)
def test_versions_required(self):
# auto-approve everything (including self.addon.current_version)
for version in Version.unfiltered.all():

Просмотреть файл

@ -73,9 +73,9 @@ class TestReviewHelperBase(TestCase):
self.user = UserProfile.objects.get(pk=10482)
self.addon = Addon.objects.get(pk=3615)
self.version = self.addon.versions.all()[0]
self.review_version = self.addon.versions.all()[0]
self.helper = self.get_helper()
self.file = self.version.file
self.file = self.review_version.file
self.create_paths()
@ -110,7 +110,8 @@ class TestReviewHelperBase(TestCase):
self.file.update(status=file_status)
if channel == amo.CHANNEL_UNLISTED:
self.make_addon_unlisted(self.addon)
self.version.reload()
if self.review_version:
self.review_version.reload()
self.file.reload()
self.helper = self.get_helper(
content_review=content_review, human_review=human_review
@ -129,7 +130,7 @@ class TestReviewHelperBase(TestCase):
def get_helper(self, content_review=False, human_review=True):
return ReviewHelper(
addon=self.addon,
version=self.version,
version=self.review_version,
user=self.user,
human_review=human_review,
content_review=content_review,
@ -173,7 +174,7 @@ class TestReviewHelper(TestReviewHelperBase):
def test_review_files(self):
version_factory(
addon=self.addon,
created=self.version.created - timedelta(days=1),
created=self.review_version.created - timedelta(days=1),
file_kw={'status': amo.STATUS_APPROVED},
)
for status in REVIEW_FILES_STATUSES:
@ -396,7 +397,7 @@ class TestReviewHelper(TestReviewHelperBase):
"""Deleted addons and addons with no versions in that channel have no
version set."""
expected = []
self.version = None
self.review_version = None
assert (
list(
self.get_review_actions(
@ -556,6 +557,7 @@ class TestReviewHelper(TestReviewHelperBase):
'public',
'reject',
'reject_multiple_versions',
'unreject_multiple_versions',
'reply',
'super',
'comment',
@ -573,7 +575,7 @@ class TestReviewHelper(TestReviewHelperBase):
def test_actions_unlisted(self):
# Just regular review permissions don't let you do much on an unlisted
# review page.
self.version.update(channel=amo.CHANNEL_UNLISTED)
self.review_version.update(channel=amo.CHANNEL_UNLISTED)
self.grant_permission(self.user, 'Addons:Review')
expected = ['reply', 'super', 'comment']
assert (
@ -653,7 +655,7 @@ class TestReviewHelper(TestReviewHelperBase):
)
# it's okay if the version is outside the blocked range though
block.update(min_version=self.version.version + '.1')
block.update(min_version=self.review_version.version + '.1')
expected = [
'public',
'reject',
@ -672,7 +674,7 @@ class TestReviewHelper(TestReviewHelperBase):
version=self.addon.current_version, verdict=amo.AUTO_APPROVED
)
version_review_flags_factory(
version=self.version, pending_rejection=datetime.now()
version=self.review_version, pending_rejection=datetime.now()
)
expected = ['reply', 'super', 'comment']
assert (
@ -693,8 +695,8 @@ class TestReviewHelper(TestReviewHelperBase):
'super',
'comment',
]
self.version = version_factory(addon=self.addon)
self.file = self.version.file
self.review_version = version_factory(addon=self.addon)
self.file = self.review_version.file
assert (
list(
@ -715,11 +717,12 @@ class TestReviewHelper(TestReviewHelperBase):
version=self.addon.current_version, verdict=amo.AUTO_APPROVED
)
version_review_flags_factory(
version=self.version, pending_rejection=datetime.now()
version=self.review_version, pending_rejection=datetime.now()
)
expected = [
'confirm_auto_approved',
'reject_multiple_versions',
'unreject_multiple_versions',
'reply',
'super',
'comment',
@ -743,12 +746,13 @@ class TestReviewHelper(TestReviewHelperBase):
'reject',
'confirm_auto_approved',
'reject_multiple_versions',
'unreject_multiple_versions',
'reply',
'super',
'comment',
]
self.version = version_factory(addon=self.addon)
self.file = self.version.file
self.review_version = version_factory(addon=self.addon)
self.file = self.review_version.file
assert (
list(
self.get_review_actions(
@ -789,9 +793,9 @@ class TestReviewHelper(TestReviewHelperBase):
def test_set_file(self):
self.file.update(datestatuschanged=yesterday)
self.helper.handler.set_file(amo.STATUS_APPROVED, self.version.file)
self.helper.handler.set_file(amo.STATUS_APPROVED, self.review_version.file)
self.file = self.version.file
self.file = self.review_version.file
assert self.file.status == amo.STATUS_APPROVED
assert self.file.datestatuschanged.date() > yesterday.date()
@ -835,8 +839,8 @@ class TestReviewHelper(TestReviewHelperBase):
self.helper.set_data(self.get_data())
base_fragment = 'To respond, please reply to this email or visit'
user = self.addon.listed_authors[0]
ActivityLogToken.objects.create(version=self.version, user=user)
uuid = self.version.token.get(user=user).uuid.hex
ActivityLogToken.objects.create(version=self.review_version, user=user)
uuid = self.review_version.token.get(user=user).uuid.hex
reply_email = f'reviewreply+{uuid}@{settings.INBOUND_EMAIL_DOMAIN}'
templates = (
@ -951,7 +955,7 @@ class TestReviewHelper(TestReviewHelperBase):
status = amo.STATUS_NOMINATED
self.setup_data(status)
AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED, weight=101
version=self.review_version, verdict=amo.AUTO_APPROVED, weight=101
)
# Make sure we have no public files
@ -985,31 +989,31 @@ class TestReviewHelper(TestReviewHelperBase):
def test_nomination_to_public_need_human_review(self):
self.setup_data(amo.STATUS_NOMINATED)
self.version.update(needs_human_review=True)
self.review_version.update(needs_human_review=True)
self.helper.handler.approve_latest_version()
self.addon.reload()
self.version.reload()
self.review_version.reload()
self.file.reload()
assert self.addon.status == amo.STATUS_APPROVED
assert self.file.status == amo.STATUS_APPROVED
assert not self.version.needs_human_review
assert not self.review_version.needs_human_review
def test_nomination_to_public_need_human_review_not_human(self):
self.setup_data(amo.STATUS_NOMINATED, human_review=False)
self.version.update(needs_human_review=True)
self.review_version.update(needs_human_review=True)
self.helper.handler.approve_latest_version()
self.addon.reload()
self.version.reload()
self.review_version.reload()
self.file.reload()
assert self.addon.status == amo.STATUS_APPROVED
assert self.file.status == amo.STATUS_APPROVED
assert self.version.needs_human_review
assert self.review_version.needs_human_review
def test_unlisted_approve_latest_version_need_human_review(self):
self.setup_data(amo.STATUS_NULL, channel=amo.CHANNEL_UNLISTED)
self.version.update(needs_human_review=True)
self.review_version.update(needs_human_review=True)
flags = version_review_flags_factory(
version=self.version,
version=self.review_version,
needs_human_review_by_mad=True,
)
AddonReviewerFlags.objects.create(
@ -1017,13 +1021,13 @@ class TestReviewHelper(TestReviewHelperBase):
)
self.helper.handler.approve_latest_version()
self.addon.reload()
self.version.reload()
self.review_version.reload()
self.file.reload()
flags.reload()
addon_flags = self.addon.reviewerflags.reload()
assert self.addon.status == amo.STATUS_NULL
assert self.file.status == amo.STATUS_APPROVED
assert not self.version.needs_human_review
assert not self.review_version.needs_human_review
assert not flags.needs_human_review_by_mad
assert not addon_flags.auto_approval_disabled_until_next_approval_unlisted
@ -1031,22 +1035,22 @@ class TestReviewHelper(TestReviewHelperBase):
self.setup_data(
amo.STATUS_NULL, channel=amo.CHANNEL_UNLISTED, human_review=False
)
self.version.update(needs_human_review=True)
self.review_version.update(needs_human_review=True)
flags = version_review_flags_factory(
version=self.version, needs_human_review_by_mad=True
version=self.review_version, needs_human_review_by_mad=True
)
AddonReviewerFlags.objects.create(
addon=self.addon, auto_approval_disabled_until_next_approval_unlisted=True
)
self.helper.handler.approve_latest_version()
self.addon.reload()
self.version.reload()
self.review_version.reload()
self.file.reload()
flags.reload()
addon_flags = self.addon.reviewerflags.reload()
assert self.addon.status == amo.STATUS_NULL
assert self.file.status == amo.STATUS_APPROVED
assert self.version.needs_human_review
assert self.review_version.needs_human_review
assert flags.needs_human_review_by_mad
# Not changed this this is not a human approval.
@ -1075,7 +1079,8 @@ class TestReviewHelper(TestReviewHelperBase):
self.sign_file_mock.reset()
self.setup_data(amo.STATUS_NOMINATED)
AutoApprovalSummary.objects.update_or_create(
version=self.version, defaults={'verdict': amo.AUTO_APPROVED, 'weight': 101}
version=self.review_version,
defaults={'verdict': amo.AUTO_APPROVED, 'weight': 101},
)
self.helper.handler.approve_latest_version()
@ -1102,7 +1107,7 @@ class TestReviewHelper(TestReviewHelperBase):
def test_old_nomination_to_public_bonus_score(self):
self.sign_file_mock.reset()
self.setup_data(amo.STATUS_NOMINATED, type=amo.ADDON_STATICTHEME)
self.version.update(nomination=self.days_ago(9))
self.review_version.update(nomination=self.days_ago(9))
self.helper.handler.approve_latest_version()
@ -1160,7 +1165,7 @@ class TestReviewHelper(TestReviewHelperBase):
def test_public_addon_with_version_awaiting_review_to_public(self):
self.sign_file_mock.reset()
self.addon.current_version.update(created=self.days_ago(1))
self.version = version_factory(
self.review_version = version_factory(
addon=self.addon,
channel=amo.CHANNEL_LISTED,
version='3.0.42',
@ -1170,10 +1175,10 @@ class TestReviewHelper(TestReviewHelperBase):
},
)
self.preamble = 'Mozilla Add-ons: Delicious Bookmarks 3.0.42'
self.file = self.version.file
self.file = self.review_version.file
self.setup_data(amo.STATUS_APPROVED)
AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED, weight=101
version=self.review_version, verdict=amo.AUTO_APPROVED, weight=101
)
self.create_paths()
AddonApprovalsCounter.objects.create(
@ -1217,13 +1222,13 @@ class TestReviewHelper(TestReviewHelperBase):
def test_public_addon_with_version_need_human_review_to_public(self):
self.old_version = self.addon.current_version
self.old_version.update(created=self.days_ago(1), needs_human_review=True)
self.version = version_factory(
self.review_version = version_factory(
addon=self.addon,
channel=amo.CHANNEL_LISTED,
version='3.0.42',
file_kw={'status': amo.STATUS_AWAITING_REVIEW},
)
self.file = self.version.file
self.file = self.review_version.file
self.setup_data(amo.STATUS_APPROVED)
self.helper.handler.approve_latest_version()
@ -1239,13 +1244,13 @@ class TestReviewHelper(TestReviewHelperBase):
AddonReviewerFlags.objects.create(
addon=self.addon, auto_approval_disabled_until_next_approval=True
)
self.version = version_factory(
self.review_version = version_factory(
addon=self.addon,
channel=amo.CHANNEL_LISTED,
version='3.0.42',
file_kw={'status': amo.STATUS_AWAITING_REVIEW},
)
self.file = self.version.file
self.file = self.review_version.file
self.setup_data(amo.STATUS_APPROVED)
self.helper.handler.approve_latest_version()
@ -1260,7 +1265,7 @@ class TestReviewHelper(TestReviewHelperBase):
def test_public_addon_with_version_awaiting_review_to_sandbox(self):
self.sign_file_mock.reset()
self.addon.current_version.update(created=self.days_ago(1))
self.version = version_factory(
self.review_version = version_factory(
addon=self.addon,
channel=amo.CHANNEL_LISTED,
version='3.0.42',
@ -1270,10 +1275,10 @@ class TestReviewHelper(TestReviewHelperBase):
},
)
self.preamble = 'Mozilla Add-ons: Delicious Bookmarks 3.0.42'
self.file = self.version.file
self.file = self.review_version.file
self.setup_data(amo.STATUS_APPROVED)
AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED, weight=101
version=self.review_version, verdict=amo.AUTO_APPROVED, weight=101
)
AddonApprovalsCounter.objects.create(addon=self.addon, counter=1)
@ -1308,14 +1313,14 @@ class TestReviewHelper(TestReviewHelperBase):
def test_public_addon_with_version_need_human_review_to_sandbox(self):
self.old_version = self.addon.current_version
self.old_version.update(created=self.days_ago(1), needs_human_review=True)
self.version = version_factory(
self.review_version = version_factory(
addon=self.addon,
channel=amo.CHANNEL_LISTED,
version='3.0.42',
needs_human_review=True,
file_kw={'status': amo.STATUS_AWAITING_REVIEW},
)
self.file = self.version.file
self.file = self.review_version.file
self.setup_data(amo.STATUS_APPROVED)
self.helper.handler.reject_latest_version()
@ -1332,14 +1337,14 @@ class TestReviewHelper(TestReviewHelperBase):
self.addon.current_version.reload()
assert self.addon.current_version.needs_human_review
self.version.reload()
assert not self.version.needs_human_review
self.review_version.reload()
assert not self.review_version.needs_human_review
def test_public_addon_confirm_auto_approval(self):
self.grant_permission(self.user, 'Addons:Review')
self.setup_data(amo.STATUS_APPROVED, file_status=amo.STATUS_APPROVED)
summary = AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED, weight=151
version=self.review_version, verdict=amo.AUTO_APPROVED, weight=151
)
assert summary.confirmed is None
self.create_paths()
@ -1362,7 +1367,7 @@ class TestReviewHelper(TestReviewHelperBase):
.filter(action=amo.LOG.CONFIRM_AUTO_APPROVED.id)
.get()
)
assert activity.arguments == [self.addon, self.version]
assert activity.arguments == [self.addon, self.review_version]
assert activity.details['comments'] == ''
# Check points awarded.
@ -1371,16 +1376,16 @@ class TestReviewHelper(TestReviewHelperBase):
def test_public_with_unreviewed_version_addon_confirm_auto_approval(self):
self.grant_permission(self.user, 'Addons:Review')
self.setup_data(amo.STATUS_APPROVED, file_status=amo.STATUS_APPROVED)
self.current_version = self.version
self.current_version = self.review_version
summary = AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED, weight=152
version=self.review_version, verdict=amo.AUTO_APPROVED, weight=152
)
self.version = version_factory(
self.review_version = version_factory(
addon=self.addon,
version='3.0',
file_kw={'status': amo.STATUS_AWAITING_REVIEW},
)
self.file = self.version.file
self.file = self.review_version.file
self.helper = self.get_helper() # To make it pick up the new version.
self.helper.set_data(self.get_data())
@ -1410,14 +1415,14 @@ class TestReviewHelper(TestReviewHelperBase):
def test_public_with_disabled_version_addon_confirm_auto_approval(self):
self.grant_permission(self.user, 'Addons:Review')
self.setup_data(amo.STATUS_APPROVED, file_status=amo.STATUS_APPROVED)
self.current_version = self.version
self.current_version = self.review_version
summary = AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED, weight=153
version=self.review_version, verdict=amo.AUTO_APPROVED, weight=153
)
self.version = version_factory(
self.review_version = version_factory(
addon=self.addon, version='3.0', file_kw={'status': amo.STATUS_DISABLED}
)
self.file = self.version.file
self.file = self.review_version.file
self.helper = self.get_helper() # To make it pick up the new version.
self.helper.set_data(self.get_data())
@ -1448,12 +1453,12 @@ class TestReviewHelper(TestReviewHelperBase):
self.grant_permission(self.user, 'Addons:Review')
self.grant_permission(self.user, 'Reviews:Admin')
self.setup_data(amo.STATUS_APPROVED, file_status=amo.STATUS_APPROVED)
self.version = version_factory(
self.review_version = version_factory(
addon=self.addon, version='3.0', file_kw={'status': amo.STATUS_APPROVED}
)
self.file = self.version.file
self.file = self.review_version.file
summary = AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED, weight=153
version=self.review_version, verdict=amo.AUTO_APPROVED, weight=153
)
for version in self.addon.versions.all():
@ -1483,7 +1488,7 @@ class TestReviewHelper(TestReviewHelperBase):
.filter(action=amo.LOG.CONFIRM_AUTO_APPROVED.id)
.get()
)
assert activity.arguments == [self.addon, self.version]
assert activity.arguments == [self.addon, self.review_version]
assert activity.details['comments'] == ''
# None of the versions should be pending rejection anymore.
@ -1523,9 +1528,9 @@ class TestReviewHelper(TestReviewHelperBase):
def test_confirm_multiple_versions_with_version_scanner_flags(self):
self.grant_permission(self.user, 'Addons:ReviewUnlisted')
self.setup_data(amo.STATUS_APPROVED, file_status=amo.STATUS_APPROVED)
self.version.update(channel=amo.CHANNEL_UNLISTED)
self.review_version.update(channel=amo.CHANNEL_UNLISTED)
flags = version_review_flags_factory(
version=self.version,
version=self.review_version,
needs_human_review_by_mad=True,
)
assert flags.needs_human_review_by_mad
@ -1565,14 +1570,14 @@ class TestReviewHelper(TestReviewHelperBase):
needs_human_review=True,
created=self.days_ago(6),
)
self.version = version_factory(
self.review_version = version_factory(
addon=self.addon,
version='5.0',
channel=amo.CHANNEL_UNLISTED,
needs_human_review=True,
created=self.days_ago(5),
)
self.file = self.version.file
self.file = self.review_version.file
self.helper = self.get_helper() # To make it pick up the new version.
data = self.get_data().copy()
data['versions'] = self.addon.versions.filter(
@ -1589,8 +1594,8 @@ class TestReviewHelper(TestReviewHelperBase):
summary.reload()
assert summary.confirmed is True
self.version.reload()
assert self.version.needs_human_review # Untouched.
self.review_version.reload()
assert self.review_version.needs_human_review # Untouched.
second_unlisted.reload()
assert not second_unlisted.needs_human_review # Cleared.
@ -1627,7 +1632,8 @@ class TestReviewHelper(TestReviewHelperBase):
message = mail.outbox[0]
assert message.subject == ('%s signed and ready to download' % self.preamble)
assert (
'%s is now signed and ready for you to download' % self.version.version
'%s is now signed and ready for you to download'
% self.review_version.version
in message.body
)
assert 'You received this email because' not in message.body
@ -1781,16 +1787,16 @@ class TestReviewHelper(TestReviewHelperBase):
assert self.addon.needs_admin_code_review
def test_nominated_review_time_set_version_approve_latest_version(self):
self.version.update(reviewed=None)
self.review_version.update(reviewed=None)
self.setup_data(amo.STATUS_NOMINATED)
self.helper.handler.approve_latest_version()
assert self.version.reload().reviewed
assert self.review_version.reload().reviewed
def test_nominated_review_time_set_version_reject_latest_version(self):
self.version.update(reviewed=None)
self.review_version.update(reviewed=None)
self.setup_data(amo.STATUS_NOMINATED)
self.helper.handler.reject_latest_version()
assert self.version.reload().reviewed
assert self.review_version.reload().reviewed
def test_nominated_review_time_set_file_approve_latest_version(self):
self.file.update(reviewed=None)
@ -1806,7 +1812,7 @@ class TestReviewHelper(TestReviewHelperBase):
def test_review_unlisted_while_a_listed_version_is_awaiting_review(self):
self.make_addon_unlisted(self.addon)
self.version.reload()
self.review_version.reload()
version_factory(
addon=self.addon,
channel=amo.CHANNEL_LISTED,
@ -1816,10 +1822,10 @@ class TestReviewHelper(TestReviewHelperBase):
assert self.get_helper()
def test_reject_multiple_versions(self):
old_version = self.version
self.version = version_factory(addon=self.addon, version='3.0')
old_version = self.review_version
self.review_version = version_factory(addon=self.addon, version='3.0')
AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED, weight=101
version=self.review_version, verdict=amo.AUTO_APPROVED, weight=101
)
self.setup_data(amo.STATUS_APPROVED, file_status=amo.STATUS_APPROVED)
@ -1838,7 +1844,7 @@ class TestReviewHelper(TestReviewHelperBase):
self.file.reload()
assert self.addon.status == amo.STATUS_NULL
assert self.addon.current_version is None
assert list(self.addon.versions.all()) == [self.version, old_version]
assert list(self.addon.versions.all()) == [self.review_version, old_version]
assert self.file.status == amo.STATUS_DISABLED
# The versions are not pending rejection.
@ -1878,14 +1884,14 @@ class TestReviewHelper(TestReviewHelperBase):
# The reviewer should have been automatically subscribed to new listed
# versions.
assert ReviewerSubscription.objects.filter(
addon=self.addon, user=self.user, channel=self.version.channel
addon=self.addon, user=self.user, channel=self.review_version.channel
).exists()
def test_reject_multiple_versions_with_delay(self):
old_version = self.version
self.version = version_factory(addon=self.addon, version='3.0')
old_version = self.review_version
self.review_version = version_factory(addon=self.addon, version='3.0')
AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED, weight=101
version=self.review_version, verdict=amo.AUTO_APPROVED, weight=101
)
self.setup_data(amo.STATUS_APPROVED, file_status=amo.STATUS_APPROVED)
@ -1912,8 +1918,8 @@ class TestReviewHelper(TestReviewHelperBase):
self.addon.reload()
self.file.reload()
assert self.addon.status == amo.STATUS_APPROVED
assert self.addon.current_version == self.version
assert list(self.addon.versions.all()) == [self.version, old_version]
assert self.addon.current_version == self.review_version
assert list(self.addon.versions.all()) == [self.review_version, old_version]
assert self.file.status == amo.STATUS_APPROVED
# The versions are now pending rejection.
@ -1958,16 +1964,16 @@ class TestReviewHelper(TestReviewHelperBase):
# The reviewer should have been automatically subscribed to new listed
# versions.
assert ReviewerSubscription.objects.filter(
addon=self.addon, user=self.user, channel=self.version.channel
addon=self.addon, user=self.user, channel=self.review_version.channel
).exists()
def test_reject_multiple_versions_except_latest(self):
old_version = self.version
old_version = self.review_version
extra_version = version_factory(addon=self.addon, version='3.1')
# Add yet another version we don't want to reject.
self.version = version_factory(addon=self.addon, version='42.0')
self.review_version = version_factory(addon=self.addon, version='42.0')
AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED, weight=91
version=self.review_version, verdict=amo.AUTO_APPROVED, weight=91
)
self.setup_data(amo.STATUS_APPROVED, file_status=amo.STATUS_APPROVED)
@ -1978,7 +1984,7 @@ class TestReviewHelper(TestReviewHelperBase):
assert self.addon.current_version.is_public()
data = self.get_data().copy()
data['versions'] = self.addon.versions.all().exclude(pk=self.version.pk)
data['versions'] = self.addon.versions.all().exclude(pk=self.review_version.pk)
self.helper.set_data(data)
self.helper.handler.reject_multiple_versions()
@ -1986,9 +1992,9 @@ class TestReviewHelper(TestReviewHelperBase):
self.file.reload()
# latest_version is still public so the add-on is still public.
assert self.addon.status == amo.STATUS_APPROVED
assert self.addon.current_version == self.version
assert self.addon.current_version == self.review_version
assert list(self.addon.versions.all().order_by('-pk')) == [
self.version,
self.review_version,
extra_version,
old_version,
]
@ -2001,7 +2007,7 @@ class TestReviewHelper(TestReviewHelperBase):
'Mozilla Add-ons: Versions disabled for Delicious Bookmarks'
)
assert 'Version(s) affected and disabled:\n3.1, 2.1.072' in message.body
log_token = ActivityLogToken.objects.filter(version=self.version).get()
log_token = ActivityLogToken.objects.filter(version=self.review_version).get()
assert log_token.uuid.hex in message.reply_to[0]
assert self.check_log_count(amo.LOG.REJECT_VERSION.id) == 2
@ -2011,9 +2017,9 @@ class TestReviewHelper(TestReviewHelperBase):
self._check_score(amo.REVIEWED_EXTENSION_MEDIUM_RISK)
def test_reject_multiple_versions_need_human_review(self):
old_version = self.version
old_version = self.review_version
old_version.update(needs_human_review=True)
self.version = version_factory(
self.review_version = version_factory(
addon=self.addon, version='3.0', needs_human_review=True
)
@ -2026,7 +2032,7 @@ class TestReviewHelper(TestReviewHelperBase):
self.file.reload()
assert self.addon.status == amo.STATUS_NULL
assert self.addon.current_version is None
assert list(self.addon.versions.all()) == [self.version, old_version]
assert list(self.addon.versions.all()) == [self.review_version, old_version]
# We rejected all versions so there aren't any left that need human
# review.
assert not self.addon.versions.filter(needs_human_review=True).exists()
@ -2034,8 +2040,8 @@ class TestReviewHelper(TestReviewHelperBase):
def test_reject_multiple_versions_content_review(self):
self.grant_permission(self.user, 'Addons:ContentReview')
old_version = self.version
self.version = version_factory(addon=self.addon, version='3.0')
old_version = self.review_version
self.review_version = version_factory(addon=self.addon, version='3.0')
self.setup_data(
amo.STATUS_APPROVED, file_status=amo.STATUS_APPROVED, content_review=True
)
@ -2055,7 +2061,7 @@ class TestReviewHelper(TestReviewHelperBase):
self.file.reload()
assert self.addon.status == amo.STATUS_NULL
assert self.addon.current_version is None
assert list(self.addon.versions.all()) == [self.version, old_version]
assert list(self.addon.versions.all()) == [self.review_version, old_version]
assert self.file.status == amo.STATUS_DISABLED
assert len(mail.outbox) == 1
@ -2077,8 +2083,8 @@ class TestReviewHelper(TestReviewHelperBase):
def test_reject_multiple_versions_content_review_with_delay(self):
self.grant_permission(self.user, 'Addons:ContentReview')
old_version = self.version
self.version = version_factory(addon=self.addon, version='3.0')
old_version = self.review_version
self.review_version = version_factory(addon=self.addon, version='3.0')
self.setup_data(
amo.STATUS_APPROVED, file_status=amo.STATUS_APPROVED, content_review=True
)
@ -2086,7 +2092,7 @@ class TestReviewHelper(TestReviewHelperBase):
# Pre-subscribe the user to new listed versions of this add-on, it
# shouldn't matter.
ReviewerSubscription.objects.create(
addon=self.addon, user=self.user, channel=self.version.channel
addon=self.addon, user=self.user, channel=self.review_version.channel
)
in_the_future = datetime.now() + timedelta(days=14)
@ -2112,8 +2118,8 @@ class TestReviewHelper(TestReviewHelperBase):
self.addon.reload()
self.file.reload()
assert self.addon.status == amo.STATUS_APPROVED
assert self.addon.current_version == self.version
assert list(self.addon.versions.all()) == [self.version, old_version]
assert self.addon.current_version == self.review_version
assert list(self.addon.versions.all()) == [self.review_version, old_version]
assert self.file.status == amo.STATUS_APPROVED
# The versions are now pending rejection.
@ -2148,13 +2154,109 @@ class TestReviewHelper(TestReviewHelperBase):
# The reviewer was already subscribed to new listed versions for this
# addon, nothing has changed.
assert ReviewerSubscription.objects.filter(
addon=self.addon, user=self.user, channel=self.version.channel
addon=self.addon, user=self.user, channel=self.review_version.channel
).exists()
def test_unreject_multiple_versions_approved_addon(self):
first_version = self.review_version
self.review_version = version_factory(
addon=self.addon, version='3.0', file_kw={'status': amo.STATUS_DISABLED}
)
self.file = self.review_version.file
# Safeguards.
assert isinstance(self.helper.handler, ReviewFiles)
assert self.addon.status == amo.STATUS_APPROVED
assert first_version.file.status == amo.STATUS_APPROVED
assert self.file.status == amo.STATUS_DISABLED
assert self.addon.current_version.is_public()
assert self.addon.current_version == first_version
data = self.get_data().copy()
data['versions'] = [self.review_version]
self.helper.set_data(data)
self.helper.handler.unreject_multiple_versions()
self.addon.reload()
self.file.reload()
assert self.addon.status == amo.STATUS_APPROVED
assert self.addon.current_version == first_version
assert list(self.addon.versions.all()) == [self.review_version, first_version]
assert self.file.status == amo.STATUS_AWAITING_REVIEW
assert len(mail.outbox) == 0
assert self.check_log_count(amo.LOG.UNREJECT_VERSION.id) == 1
def test_unreject_multiple_versions_with_unlisted(self):
old_version = self.review_version
self.review_version = version_factory(addon=self.addon, version='3.0')
self.file = self.review_version.file
self.setup_data(
amo.STATUS_NULL,
file_status=amo.STATUS_DISABLED,
channel=amo.CHANNEL_UNLISTED,
)
# Safeguards.
assert isinstance(self.helper.handler, ReviewUnlisted)
assert self.addon.status == amo.STATUS_NULL
assert old_version.file.status == amo.STATUS_APPROVED
assert self.file.status == amo.STATUS_DISABLED
assert self.addon.current_version is None
data = self.get_data().copy()
data['versions'] = [self.review_version]
self.helper.set_data(data)
self.helper.handler.unreject_multiple_versions()
self.addon.reload()
self.file.reload()
assert self.addon.status == amo.STATUS_NULL
assert self.addon.current_version is None
assert list(self.addon.versions.all()) == [self.review_version, old_version]
assert self.file.status == amo.STATUS_AWAITING_REVIEW
assert len(mail.outbox) == 0
assert self.check_log_count(amo.LOG.UNREJECT_VERSION.id) == 1
def test_unreject_multiple_versions_incomplete_addon(self):
old_version = self.review_version
old_version.file.update(status=amo.STATUS_DISABLED)
self.review_version = version_factory(
addon=self.addon, version='3.0', file_kw={'status': amo.STATUS_DISABLED}
)
self.file = self.review_version.file
self.setup_data(amo.STATUS_NULL, file_status=amo.STATUS_DISABLED)
# Safeguards.
assert isinstance(self.helper.handler, ReviewFiles)
assert self.addon.status == amo.STATUS_NULL
assert old_version.file.status == amo.STATUS_DISABLED
assert self.file.status == amo.STATUS_DISABLED
assert self.addon.current_version is None
data = self.get_data().copy()
data['versions'] = [self.review_version]
self.helper.set_data(data)
self.helper.handler.unreject_multiple_versions()
self.addon.reload()
self.file.reload()
assert self.addon.status == amo.STATUS_NOMINATED
assert self.addon.current_version == self.review_version
assert list(self.addon.versions.all()) == [self.review_version, old_version]
assert self.file.status == amo.STATUS_AWAITING_REVIEW
assert len(mail.outbox) == 0
assert self.check_log_count(amo.LOG.UNREJECT_VERSION.id) == 1
def test_approve_multiple_versions_unlisted(self):
old_version = self.version
old_version = self.review_version
self.make_addon_unlisted(self.addon)
self.version = version_factory(
self.review_version = version_factory(
addon=self.addon,
version='3.0',
channel=amo.CHANNEL_UNLISTED,
@ -2181,7 +2283,7 @@ class TestReviewHelper(TestReviewHelperBase):
self.file.reload()
assert self.addon.status == amo.STATUS_NULL
assert self.addon.current_version is None
assert list(self.addon.versions.all()) == [self.version, old_version]
assert list(self.addon.versions.all()) == [self.review_version, old_version]
assert self.file.status == amo.STATUS_APPROVED
# unlisted auto approvals should be enabled again
@ -2212,16 +2314,16 @@ class TestReviewHelper(TestReviewHelperBase):
assert logs[0].created == logs[1].created
def test_reject_multiple_versions_unlisted(self):
old_version = self.version
old_version = self.review_version
self.make_addon_unlisted(self.addon)
self.version = version_factory(
self.review_version = version_factory(
addon=self.addon,
version='3.0',
channel=amo.CHANNEL_UNLISTED,
file_kw={'status': amo.STATUS_AWAITING_REVIEW},
)
AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED, weight=101
version=self.review_version, verdict=amo.AUTO_APPROVED, weight=101
)
self.setup_data(amo.STATUS_NULL, file_status=amo.STATUS_AWAITING_REVIEW)
@ -2239,7 +2341,7 @@ class TestReviewHelper(TestReviewHelperBase):
self.file.reload()
assert self.addon.status == amo.STATUS_NULL
assert self.addon.current_version is None
assert list(self.addon.versions.all()) == [self.version, old_version]
assert list(self.addon.versions.all()) == [self.review_version, old_version]
assert self.file.status == amo.STATUS_DISABLED
# unlisted auto approvals should be disabled until the next manual approval.
@ -2275,9 +2377,9 @@ class TestReviewHelper(TestReviewHelperBase):
def _test_reject_multiple_versions_delayed(self, content_review):
# Do a rejection with delay.
original_user = self.user
self.version = version_factory(addon=self.addon, version='3.0')
self.review_version = version_factory(addon=self.addon, version='3.0')
AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED, weight=101
version=self.review_version, verdict=amo.AUTO_APPROVED, weight=101
)
self.setup_data(
amo.STATUS_APPROVED,
@ -2356,7 +2458,7 @@ class TestReviewHelper(TestReviewHelperBase):
amo.STATUS_APPROVED, file_status=amo.STATUS_APPROVED, content_review=True
)
summary = AutoApprovalSummary.objects.create(
version=self.version, verdict=amo.AUTO_APPROVED
version=self.review_version, verdict=amo.AUTO_APPROVED
)
self.create_paths()
@ -2380,7 +2482,7 @@ class TestReviewHelper(TestReviewHelperBase):
.filter(action=amo.LOG.APPROVE_CONTENT.id)
.get()
)
assert activity.arguments == [self.addon, self.version]
assert activity.arguments == [self.addon, self.review_version]
assert activity.details['comments'] == ''
# Check points awarded.
@ -2393,7 +2495,7 @@ class TestReviewHelper(TestReviewHelperBase):
self.addon.get_dev_url('versions')
)
self.version.update(channel=amo.CHANNEL_UNLISTED)
self.review_version.update(channel=amo.CHANNEL_UNLISTED)
context_data = self.helper.handler.get_context_data()
assert context_data['dev_versions_url'] == absolutify(
reverse('devhub.addons.versions', args=[self.addon.id])
@ -2467,8 +2569,8 @@ class TestReviewHelper(TestReviewHelperBase):
assert self.addon.promoted_group() == STRATEGIC
def _test_block_multiple_unlisted_versions(self, redirect_url):
old_version = self.version
self.version = version_factory(
old_version = self.review_version
self.review_version = version_factory(
addon=self.addon, version='3.0', needs_human_review=True
)
self.setup_data(
@ -2478,7 +2580,7 @@ class TestReviewHelper(TestReviewHelperBase):
)
# Add a needs_human_review_by_mad flag that should be cleared later.
version_review_flags_factory(
version=self.version, needs_human_review_by_mad=True
version=self.review_version, needs_human_review_by_mad=True
)
# Safeguards.
assert isinstance(self.helper.handler, ReviewUnlisted)
@ -2506,7 +2608,7 @@ class TestReviewHelper(TestReviewHelperBase):
# We should have set redirect_url to point to the Block admin page
if '%s' in redirect_url:
redirect_url = redirect_url % (old_version.pk, self.version.pk)
redirect_url = redirect_url % (old_version.pk, self.review_version.pk)
assert self.helper.redirect_url == redirect_url
def test_pending_blocklistsubmission_multiple_unlisted_versions(self):
@ -2564,9 +2666,9 @@ class TestReviewHelperSigning(TestReviewHelperBase):
file_kw={'filename': 'webextension.xpi'},
users=[self.user],
)
self.version = self.addon.versions.all()[0]
self.review_version = self.addon.versions.all()[0]
self.helper = self.get_helper()
self.file = self.version.file
self.file = self.review_version.file
def test_nomination_to_public(self):
self.setup_data(amo.STATUS_NOMINATED)

Просмотреть файл

@ -2864,6 +2864,7 @@ class TestReview(ReviewBase):
'public',
'reject',
'reject_multiple_versions',
'unreject_multiple_versions',
'reply',
'super',
'comment',
@ -5008,6 +5009,85 @@ class TestReview(ReviewBase):
assert version.pending_rejection
self.assertCloseToNow(version.pending_rejection, now=in_the_future)
def test_unreject_multiple_versions(self):
old_version = self.version
version_factory(addon=self.addon, version='2.99')
old_version.file.update(status=amo.STATUS_DISABLED)
self.version = version_factory(
addon=self.addon, version='3.0', file_kw={'status': amo.STATUS_DISABLED}
)
GroupUser.objects.filter(user=self.reviewer).all().delete()
self.grant_permission(self.reviewer, 'Addons:Review')
self.grant_permission(self.reviewer, 'Reviews:Admin')
assert self.addon.status == amo.STATUS_APPROVED
response = self.client.post(
self.url,
{
'action': 'unreject_multiple_versions',
'versions': [old_version.pk, self.version.pk],
},
)
assert response.status_code == 302
for version in [old_version, self.version]:
file_ = version.file.reload()
assert file_.status == amo.STATUS_AWAITING_REVIEW
assert self.addon.reload().status == amo.STATUS_APPROVED
def test_unreject_multiple_versions_to_nominated(self):
old_version = self.version
old_version.file.update(status=amo.STATUS_DISABLED)
self.version = version_factory(
addon=self.addon, version='3.0', file_kw={'status': amo.STATUS_DISABLED}
)
GroupUser.objects.filter(user=self.reviewer).all().delete()
self.grant_permission(self.reviewer, 'Addons:Review')
self.grant_permission(self.reviewer, 'Reviews:Admin')
assert self.addon.status == amo.STATUS_NULL
response = self.client.post(
self.url,
{
'action': 'unreject_multiple_versions',
'versions': [old_version.pk, self.version.pk],
},
)
assert response.status_code == 302
for version in [old_version, self.version]:
file_ = version.file.reload()
assert file_.status == amo.STATUS_AWAITING_REVIEW
assert self.addon.reload().status == amo.STATUS_NOMINATED
def test_unreject_multiple_versions_with_unlisted(self):
old_version = self.version
old_version.file.update(status=amo.STATUS_DISABLED)
self.version = version_factory(
addon=self.addon, version='3.0', file_kw={'status': amo.STATUS_DISABLED}
)
self.make_addon_unlisted(self.addon)
GroupUser.objects.filter(user=self.reviewer).all().delete()
self.grant_permission(self.reviewer, 'Addons:Review')
self.grant_permission(self.reviewer, 'Reviews:Admin')
self.grant_permission(self.reviewer, 'Addons:ReviewUnlisted')
assert self.addon.status == amo.STATUS_NULL
unlisted_url = reverse('reviewers.review', args=['unlisted', self.addon.pk])
response = self.client.post(
unlisted_url,
{
'action': 'unreject_multiple_versions',
'versions': [old_version.pk, self.version.pk],
},
)
assert response.status_code == 302
for version in [old_version, self.version]:
file_ = version.file.reload()
assert file_.status == amo.STATUS_AWAITING_REVIEW
assert self.addon.reload().status == amo.STATUS_NULL
def test_block_multiple_versions(self):
self.url = reverse('reviewers.review', args=('unlisted', self.addon.pk))
old_version = self.version

Просмотреть файл

@ -561,9 +561,11 @@ class ReviewHelper:
is_appropriate_reviewer_post_review = acl.action_allowed_for(
self.user, permission_post_review
)
is_admin_reviewer = is_appropriate_reviewer and acl.action_allowed_for(
self.user, amo.permissions.REVIEWS_ADMIN
)
addon_is_complete_and_not_disabled = self.addon.status not in (
amo.STATUS_NULL,
addon_is_not_disabled_or_deleted = self.addon.status not in (
amo.STATUS_DELETED,
amo.STATUS_DISABLED,
)
@ -571,9 +573,8 @@ class ReviewHelper:
self.addon.status == amo.STATUS_NULL and version_is_unlisted
)
addon_is_reviewable = (
addon_is_complete_and_not_disabled
or addon_is_incomplete_and_version_is_unlisted
)
addon_is_not_disabled_or_deleted and self.addon.status != amo.STATUS_NULL
) or addon_is_incomplete_and_version_is_unlisted
version_is_unreviewed = self.version and self.version.is_unreviewed
addon_is_valid = self.addon.is_public() or self.addon.is_unreviewed()
addon_is_valid_and_version_is_listed = (
@ -689,7 +690,7 @@ class ReviewHelper:
'method': self.handler.approve_multiple_versions,
'label': _('Approve Multiple Versions'),
'minimal': True,
'versions': True,
'multiple_versions': True,
'details': _(
'This will approve the selected versions. '
'The comments will be sent to the developer.'
@ -708,7 +709,7 @@ class ReviewHelper:
# or (unlisted and) awaiting review
or self.version.file.status == amo.STATUS_AWAITING_REVIEW
),
'versions': True,
'multiple_versions': True,
'details': _(
'This will reject the selected versions. '
'The comments will be sent to the developer.'
@ -717,11 +718,23 @@ class ReviewHelper:
'allows_reasons': not is_static_theme,
'requires_reasons': not is_static_theme,
}
actions['unreject_multiple_versions'] = {
'method': self.handler.unreject_multiple_versions,
'label': _('Un-Reject Versions'),
'minimal': True,
'multiple_versions': True,
'details': _(
'This will un-reject the selected versions without notifying the '
'developer.'
),
'comments': False,
'available': (addon_is_not_disabled_or_deleted and is_admin_reviewer),
}
actions['block_multiple_versions'] = {
'method': self.handler.block_multiple_versions,
'label': _('Block Multiple Versions'),
'minimal': True,
'versions': True,
'multiple_versions': True,
'comments': False,
'details': _(
'This will disable the selected approved '
@ -736,7 +749,7 @@ class ReviewHelper:
'method': self.handler.confirm_multiple_versions,
'label': _('Confirm Multiple Versions'),
'minimal': True,
'versions': True,
'multiple_versions': True,
'details': _(
'This will confirm approval of the selected '
'versions without notifying the developer.'
@ -1360,6 +1373,37 @@ class ReviewBase:
content_review=self.content_review,
)
def unreject_multiple_versions(self):
"""Un-reject a list of versions."""
# self.version and self.file won't point to the versions we want to
# modify in this action, so set them to None before finding the right
# versions.
self.version = None
self.file = None
now = datetime.now()
# we're only supporting non-automated reviews right now:
assert self.human_review
log.info(
'Making %s versions %s awaiting review (not disabled)'
% (self.addon, ', '.join(str(v.pk) for v in self.data['versions']))
)
for version in self.data['versions']:
self.set_file(amo.STATUS_AWAITING_REVIEW, version.file)
self.log_action(
action=amo.LOG.UNREJECT_VERSION,
version=version,
file=version.file,
timestamp=now,
user=self.user,
)
if self.data['versions']:
# if these are listed versions then the addon status may need updating
self.addon.update_nominated_status(self.user)
def notify_about_auto_approval_delay(self, version):
"""Notify developers of the add-on when their version has not been
auto-approved for a while."""