Beispiel #1
0
    def test_get_rapid_experiment_recipe_returns_404_for_draft(self):
        experiment = ExperimentRapidFactory.create_with_status(
            Experiment.STATUS_DRAFT)

        response = self.client.get(
            reverse(
                "experiment-rapid-recipe-detail",
                kwargs={"recipe_slug": experiment.recipe_slug},
            ), )

        self.assertEqual(response.status_code, 404)
Beispiel #2
0
    def test_serializer_outputs_expected_schema_for_rejected_experiment(self):
        owner = UserFactory(email="*****@*****.**")
        experiment = ExperimentRapidFactory.create_with_status(
            Experiment.STATUS_ACCEPTED,
            owner=owner,
            name="rapid experiment",
            slug="rapid-experiment",
            objectives="gotta go fast",
            audience="all_english",
            features=["picture_in_picture"],
        )

        changelog = ExperimentChangeLog.objects.create(
            old_status=Experiment.STATUS_ACCEPTED,
            new_status=Experiment.STATUS_REJECTED,
            message="It's no good",
            experiment=experiment,
            changed_by=owner,
        )

        experiment.status = Experiment.STATUS_REJECTED
        experiment.save()

        serializer = ExperimentRapidSerializer(experiment)

        serializer_data = serializer.data
        serializer_data.pop("variants")

        self.maxDiff = None
        self.assertDictEqual(
            serializer_data,
            {
                "audience": "all_english",
                "bugzilla_url": "{bug_host}show_bug.cgi?id={bug_id}".format(
                    bug_host=settings.BUGZILLA_HOST, bug_id=experiment.bugzilla_id
                ),
                "features": ["picture_in_picture"],
                "firefox_channel": experiment.firefox_channel,
                "firefox_min_version": experiment.firefox_min_version,
                "monitoring_dashboard_url": experiment.monitoring_dashboard_url,
                "name": "rapid experiment",
                "objectives": "gotta go fast",
                "owner": "*****@*****.**",
                "reject_feedback": {
                    "changed_on": changelog.changed_on.isoformat().replace("+00:00", "Z"),
                    "message": "It's no good",
                },
                "slug": "rapid-experiment",
                "recipe_slug": experiment.recipe_slug,
                "status": Experiment.STATUS_REJECTED,
            },
        )
Beispiel #3
0
    def test_serializer_returns_error_for_non_unique_slug(self):
        ExperimentRapidFactory.create(name="non unique slug", slug="non-unique-slug")

        data = {
            "name": "non. unique slug",
            "objectives": "gotta go fast",
            "audience": "all_english",
            "features": ["picture_in_picture", "pinned_tabs"],
            "firefox_min_version": "80.0",
            "firefox_channel": Experiment.CHANNEL_RELEASE,
            "variants": self.variants_data,
        }

        serializer = ExperimentRapidSerializer(
            data=data, context={"request": self.request}
        )
        self.assertFalse(serializer.is_valid())

        self.assertIn(
            "Name maps to a pre-existing slug, please choose another name",
            serializer.errors["name"],
        )
Beispiel #4
0
    def test_serializer_outputs_expected_schema_for_live_experiment(self):
        owner = UserFactory(email="*****@*****.**")
        experiment = ExperimentRapidFactory.create_with_status(
            Experiment.STATUS_LIVE,
            owner=owner,
            name="rapid experiment",
            slug="rapid-experiment",
            objectives="gotta go fast",
            audience="all_english",
            features=["picture_in_picture"],
        )

        serializer = ExperimentRapidSerializer(experiment)
        serializer_data = serializer.data
        serializer_variants_data = serializer_data.pop("variants")

        self.maxDiff = None
        self.assertDictEqual(
            serializer_data,
            {
                "audience": "all_english",
                "bugzilla_url": "{bug_host}show_bug.cgi?id={bug_id}".format(
                    bug_host=settings.BUGZILLA_HOST, bug_id=experiment.bugzilla_id
                ),
                "features": ["picture_in_picture"],
                "firefox_min_version": experiment.firefox_min_version,
                "firefox_channel": experiment.firefox_channel,
                "monitoring_dashboard_url": experiment.monitoring_dashboard_url,
                "name": "rapid experiment",
                "objectives": "gotta go fast",
                "owner": "*****@*****.**",
                "reject_feedback": None,
                "slug": "rapid-experiment",
                "recipe_slug": experiment.recipe_slug,
                "status": Experiment.STATUS_LIVE,
            },
        )

        for variant in experiment.variants.all():
            variant_data = OrderedDict(
                {
                    "id": variant.id,
                    "description": variant.description,
                    "is_control": variant.is_control,
                    "name": variant.name,
                    "ratio": variant.ratio,
                    "value": variant.value,
                }
            )
            self.assertIn(variant_data, serializer_variants_data)
Beispiel #5
0
    def test_serializer_rejects_invalid_state_transition(self):
        experiment = ExperimentRapidFactory.create_with_status(
            Experiment.STATUS_REVIEW, type=Experiment.TYPE_RAPID
        )

        data = {
            "status": Experiment.STATUS_DRAFT,
        }

        serializer = ExperimentRapidStatusSerializer(
            instance=experiment, data=data, context={"request": self.request}
        )

        self.assertFalse(serializer.is_valid())
Beispiel #6
0
    def test_get_rapid_experiment_recipe_returns_recipe_info_for_experiment(
            self):
        experiment = ExperimentRapidFactory.create_with_status(
            Experiment.STATUS_LIVE)

        response = self.client.get(
            reverse(
                "experiment-rapid-recipe-detail",
                kwargs={"recipe_slug": experiment.recipe_slug},
            ), )

        self.assertEqual(response.status_code, 200)
        json_data = json.loads(response.content)
        serialized_experiment = ExperimentRapidRecipeSerializer(
            experiment).data

        self.maxDiff = None
        self.assertEqual(serialized_experiment, json_data)
Beispiel #7
0
    def test_serializer_updates_status(self):
        experiment = ExperimentRapidFactory.create_with_status(Experiment.STATUS_DRAFT)

        data = {
            "status": Experiment.STATUS_REVIEW,
        }

        serializer = ExperimentRapidStatusSerializer(
            instance=experiment, data=data, context={"request": self.request}
        )

        self.assertTrue(serializer.is_valid())
        self.assertEqual(experiment.changes.count(), 1)

        experiment = serializer.save()

        self.assertEqual(experiment.status, Experiment.STATUS_REVIEW)
        self.assertEqual(experiment.changes.count(), 2)
Beispiel #8
0
    def test_get_detail_returns_data_for_rapid_experiment(self):
        user_email = "*****@*****.**"
        owner = UserFactory(email=user_email)
        experiment = ExperimentRapidFactory.create_with_status(
            Experiment.STATUS_DRAFT,
            owner=owner,
        )

        response = self.client.get(
            reverse("experiments-rapid-detail",
                    kwargs={"slug": experiment.slug}),
            **{settings.OPENIDC_EMAIL_HEADER: user_email},
        )

        self.assertEqual(response.status_code, 200)

        json_data = json.loads(response.content)
        serialized_experiment = ExperimentRapidSerializer(experiment).data
        self.assertDictEqual(serialized_experiment, json_data)
Beispiel #9
0
    def test_serializer_update_experiment_does_not_throw_slug_err(self):
        experiment = ExperimentRapidFactory.create(
            name="non unique slug", slug="non-unique-slug"
        )

        data = {
            "name": "non unique slug",
            "objectives": "gotta go fast",
            "audience": "all_english",
            "features": ["picture_in_picture", "pinned_tabs"],
            "firefox_min_version": "80.0",
            "firefox_channel": Experiment.CHANNEL_RELEASE,
            "variants": self.variants_data,
        }

        serializer = ExperimentRapidSerializer(
            data=data, context={"request": self.request}, instance=experiment
        )
        self.assertTrue(serializer.is_valid())
Beispiel #10
0
    def test_request_review_updates_status_creates_changelog(self):
        user_email = "*****@*****.**"
        experiment = ExperimentRapidFactory.create_with_status(
            Experiment.STATUS_DRAFT,
            name="rapid experiment",
            slug="rapid-experiment",
            objectives="gotta go fast",
        )
        self.assertEqual(experiment.changes.count(), 1)

        response = self.client.post(
            reverse("experiments-rapid-request-review",
                    kwargs={"slug": experiment.slug}),
            **{settings.OPENIDC_EMAIL_HEADER: user_email},
        )

        self.assertEqual(response.status_code, 200)
        experiment = Experiment.objects.get()
        self.assertEqual(experiment.status, Experiment.STATUS_REVIEW)
        self.assertEqual(experiment.changes.count(), 2)
Beispiel #11
0
    def test_list_view_serializes_experiments(self):
        experiments = []

        for status, _ in Experiment.STATUS_CHOICES:
            experiment = ExperimentRapidFactory.create_with_status(status)

            if status not in [
                    Experiment.STATUS_DRAFT, Experiment.STATUS_REVIEW
            ]:
                experiments.append(experiment)

        response = self.client.get(reverse("experiment-rapid-recipe-list"), )
        self.assertEqual(response.status_code, 200)

        json_data = json.loads(response.content)
        json_slugs = set([d["id"] for d in json_data])
        expected_slugs = set(e.recipe_slug for e in experiments)
        self.assertEqual(json_slugs, expected_slugs)

        json_data_names = set(
            [d["arguments"]["userFacingName"] for d in json_data])
        expected_names = set(e.name for e in experiments)
        self.assertEqual(json_data_names, expected_names)
Beispiel #12
0
    def test_post_detail_edits_rapid_experiment(self):
        user_email = "*****@*****.**"
        audience = "us_only"
        features = ["picture_in_picture", "pinned_tabs"]

        owner = UserFactory(email=user_email)
        experiment = ExperimentRapidFactory.create_with_status(
            Experiment.STATUS_DRAFT,
            owner=owner,
            name="rapid experiment",
            slug="rapid-experiment",
            objectives="gotta go fast",
            audience=audience,
            features=features,
            firefox_min_version="79.0",
            firefox_channel=Experiment.CHANNEL_NIGHTLY,
        )

        control_variant = experiment.variants.get(is_control=True)
        treatment_variant = experiment.variants.get(is_control=False)

        data = json.dumps({
            "name":
            "new name",
            "objectives":
            "new hypothesis",
            "audience":
            audience,
            "features":
            features,
            "firefox_min_version":
            "80.0",
            "firefox_channel":
            Experiment.CHANNEL_RELEASE,
            "variants": [
                {
                    "id": control_variant.id,
                    "slug": "control",
                    "name": "control",
                    "ratio": 50,
                    "description": "a variant",
                    "is_control": True,
                },
                {
                    "id": treatment_variant.id,
                    "slug": "variant",
                    "name": "variant",
                    "ratio": 50,
                    "description": "a variant",
                    "is_control": False,
                },
            ],
        })

        response = self.client.put(
            reverse("experiments-rapid-detail",
                    kwargs={"slug": experiment.slug}),
            data,
            content_type="application/json",
            **{settings.OPENIDC_EMAIL_HEADER: user_email},
        )

        self.assertEqual(response.status_code, 200)
        experiment = Experiment.objects.get()
        self.assertEqual(experiment.owner.email, user_email)
        self.assertEqual(experiment.name, "new name")
        self.assertEqual(experiment.slug, "rapid-experiment")
        self.assertEqual(experiment.objectives, "new hypothesis")
        self.assertEqual(experiment.audience, audience)
        self.assertEqual(experiment.features, features)
        self.assertEqual(experiment.firefox_min_version, "80.0")
        self.assertEqual(experiment.firefox_channel,
                         Experiment.CHANNEL_RELEASE)
Beispiel #13
0
    def test_serializer_outputs_expected_schema_for_live(self):
        audience = "us_only"
        features = ["pinned_tabs", "picture_in_picture"]
        experiment = ExperimentRapidFactory.create_with_status(
            Experiment.STATUS_LIVE,
            audience=audience,
            features=features,
            firefox_channel=Experiment.CHANNEL_RELEASE,
            firefox_min_version="80.0",
        )

        serializer = ExperimentRapidRecipeSerializer(experiment)
        data = serializer.data

        arguments = data.pop("arguments")
        branches = arguments.pop("branches")

        self.assertDictEqual(
            data,
            {
                "id":
                experiment.recipe_slug,
                "filter_expression":
                "env.version|versionCompare('80.!') >= 0",
                "targeting":
                f'[userId, "{experiment.recipe_slug}"]'
                "|bucketSample(0, 100, 10000) "
                "&& localeLanguageCode == 'en' && region == 'US' "
                "&& browserSettings.update.channel == 'release'",
                "enabled":
                True,
            },
        )

        self.assertDictEqual(
            dict(arguments),
            {
                "userFacingName": experiment.name,
                "userFacingDescription": experiment.public_description,
                "slug": experiment.recipe_slug,
                "active": True,
                "isEnrollmentPaused": False,
                "endDate": experiment.end_date.isoformat(),
                "proposedEnrollment": experiment.proposed_enrollment,
                "features": features,
                "referenceBranch": "control",
                "startDate": experiment.start_date.isoformat(),
                "bucketConfig": {
                    "count": experiment.bucket.count,
                    "namespace": experiment.bucket.namespace.name,
                    "randomizationUnit": "userId",
                    "start": experiment.bucket.start,
                    "total": experiment.bucket.namespace.total,
                },
            },
        )
        converted_branches = [dict(branch) for branch in branches]
        self.assertEqual(
            converted_branches,
            [
                {
                    "ratio": 33,
                    "slug": "treatment",
                    "value": None
                },
                {
                    "ratio": 33,
                    "slug": "control",
                    "value": None
                },
            ],
        )
Beispiel #14
0
    def test_serializer_updates_experiment_and_creates_changelog(self):
        owner = UserFactory(email="*****@*****.**")
        experiment = ExperimentRapidFactory.create_with_status(
            Experiment.STATUS_DRAFT,
            owner=owner,
            name="rapid experiment",
            slug="rapid-experiment",
            objectives="gotta go fast",
            audience="us_only",
            features=["picture_in_picture", "pinned_tabs"],
            firefox_channel=Experiment.CHANNEL_RELEASE,
            firefox_min_version="79.0",
        )
        experiment.variants.all().delete()
        variant = ExperimentVariantFactory.create(experiment=experiment)

        self.assertEqual(experiment.changes.count(), 1)
        data = {
            "name": "changing the name",
            "objectives": "changing objectives",
            "audience": "all_english",
            "features": ["pinned_tabs"],
            "firefox_channel": Experiment.CHANNEL_NIGHTLY,
            "firefox_min_version": "80.0",
            "variants": [
                {
                    "id": variant.id,
                    "name": "something else",
                    "description": "something",
                    "is_control": True,
                    "ratio": 50,
                    "value": "something",
                },
                {
                    "name": "variant1",
                    "description": "variant1 description",
                    "is_control": False,
                    "ratio": 50,
                    "value": "variant value",
                },
            ],
        }

        serializer = ExperimentRapidSerializer(
            instance=experiment, data=data, context={"request": self.request}
        )

        self.assertTrue(serializer.is_valid())
        experiment = serializer.save()
        self.assertEqual(experiment.changes.count(), 2)

        changelog = experiment.changes.latest()
        self.assertEqual(changelog.old_status, Experiment.STATUS_DRAFT)
        self.assertEqual(changelog.new_status, Experiment.STATUS_DRAFT)
        self.assertEqual(
            set(changelog.changed_values.keys()),
            set(
                [
                    "audience",
                    "features",
                    "firefox_channel",
                    "firefox_min_version",
                    "name",
                    "objectives",
                    "public_description",
                    "variants",
                ]
            ),
        )