Repository: mozmeao/snippets-service

Issue:
Allow ASRSnippets to get marked `for_qa`
For #845 we want to have a set of snippets on both Production and Staging that will be used for QA. 

To avoid accidental changes to those snippets and also to avoid confusing Snippet Editos, those ASRSnippets will be only visible to superadmins.

A new ASRSnippet field `for_qa`, editable only by superadmins, will identify snippets for QA.


Discussion:

Resolving patch:
diff --git a/snippets/base/admin/adminmodels.py b/snippets/base/admin/adminmodels.py
--- a/snippets/base/admin/adminmodels.py
+++ b/snippets/base/admin/adminmodels.py
@@ -181,7 +181,7 @@ class ASRSnippetAdmin(admin.ModelAdmin):
             'fields': ('campaign', 'target', ('publish_start', 'publish_end'), 'locales', 'weight',)
         }),
         ('Other Info', {
-            'fields': ('uuid', ('created', 'modified')),
+            'fields': ('uuid', ('created', 'modified'), 'for_qa'),
             'classes': ('collapse',)
         }),
     )
@@ -223,6 +223,17 @@ def change_view(self, request, *args, **kwargs):
             request.POST = post_data
         return super().change_view(request, *args, **kwargs)
 
+    def get_readonly_fields(self, request, obj):
+        if not request.user.is_superuser:
+            return self.readonly_fields + ('for_qa',)
+        return self.readonly_fields
+
+    def get_queryset(self, request):
+        queryset = super().get_queryset(request)
+        if request.user.is_superuser:
+            return queryset
+        return queryset.filter(for_qa=False)
+
 
 class CampaignAdmin(admin.ModelAdmin):
     readonly_fields = ('created', 'modified', 'creator',)
diff --git a/snippets/base/migrations/0059_auto_20181119_1313.py b/snippets/base/migrations/0059_auto_20181119_1313.py
new file mode 100644
--- /dev/null
+++ b/snippets/base/migrations/0059_auto_20181119_1313.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.1.3 on 2018-11-19 13:13
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('base', '0058_snippet_creator'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='asrsnippet',
+            name='for_qa',
+            field=models.BooleanField(blank=True, default=False, help_text='Snippet used in QA Testing. Do not remove or unpublish.', verbose_name='For QA'),
+        ),
+    ]
diff --git a/snippets/base/models.py b/snippets/base/models.py
--- a/snippets/base/models.py
+++ b/snippets/base/models.py
@@ -630,6 +630,12 @@ class ASRSnippet(django_mysql.models.Model):
         choices=SNIPPET_WEIGHTS, default=100,
         help_text='How often should this snippet be shown to users?')
 
+    for_qa = models.BooleanField(
+        default=False, blank=True, null=False,
+        help_text='Snippet used in QA Testing. Do not remove or unpublish.',
+        verbose_name='For QA',
+    )
+
     objects = ASRSnippetManager()
 
     class Meta:


Test patch:
diff --git a/snippets/base/tests/test_admin.py b/snippets/base/tests/test_admin.py
--- a/snippets/base/tests/test_admin.py
+++ b/snippets/base/tests/test_admin.py
@@ -1,3 +1,4 @@
+from django.contrib.admin.sites import AdminSite
 from django.contrib.auth.models import User
 from django.test.client import RequestFactory
 
@@ -6,7 +7,8 @@
 from snippets.base.admin import ASRSnippetAdmin, SnippetAdmin, SnippetTemplateAdmin
 from snippets.base.models import (STATUS_CHOICES, ASRSnippet, Snippet, SnippetTemplate,
                                   SnippetTemplateVariable)
-from snippets.base.tests import SnippetTemplateFactory, SnippetTemplateVariableFactory, TestCase
+from snippets.base.tests import (ASRSnippetFactory, SnippetTemplateFactory,
+                                 SnippetTemplateVariableFactory, TestCase, UserFactory)
 
 
 class SnippetAdminTests(TestCase):
@@ -146,7 +148,7 @@ def setUp(self):
         self.factory = RequestFactory()
         self.model_admin = ASRSnippetAdmin(ASRSnippet, None)
         self.model_admin.admin_site = Mock()
-        self.user = User.objects.get_or_create(username='foo', email='foo@example.com')[0]
+        self.user = UserFactory()
 
     def test_save_as_published(self):
         request = self.factory.post('/', data={
@@ -177,3 +179,35 @@ def test_normal_save_published(self):
             change_view_mock.assert_called_with(request, 999)
             request = change_view_mock.call_args[0][0]
             self.assertEqual(request.POST['status'], str(STATUS_CHOICES['Published']))
+
+    def test_get_readonly_fields(self):
+        asrsnippet = ASRSnippetFactory()
+        request = self.factory.get('/')
+        admin = ASRSnippetAdmin(ASRSnippet, AdminSite())
+
+        # Not Super User
+        request.user = UserFactory(is_superuser=False)
+        readonly_fields = admin.get_readonly_fields(request, asrsnippet)
+        self.assertTrue('for_qa' in readonly_fields)
+
+        # SuperUser
+        request.user = UserFactory(is_superuser=True)
+        readonly_fields = admin.get_readonly_fields(request, asrsnippet)
+        self.assertTrue('for_qa' not in readonly_fields)
+
+    def test_get_queryset(self):
+        snippets = ASRSnippetFactory.create_batch(2)
+        qa_snippets = ASRSnippetFactory.create_batch(2, for_qa=True)
+        request = self.factory.get('/')
+        admin = ASRSnippetAdmin(ASRSnippet, AdminSite())
+
+        # Not Super User
+        request.user = UserFactory(is_superuser=False)
+        queryset = admin.get_queryset(request)
+        self.assertEqual(set(snippets), set(queryset.all()))
+
+        # SuperUser
+        request.user = UserFactory(is_superuser=True)
+        queryset = admin.get_queryset(request)
+
+        self.assertEqual(set(snippets + qa_snippets), set(queryset.all()))