Exemple #1
0
    def test_push_experiment_to_kinto_sends_desktop_experiment_data_and_sets_accepted(
        self, ):
        experiment = NimbusExperimentFactory.create_with_lifecycle(
            NimbusExperimentFactory.Lifecycles.LAUNCH_APPROVE,
            application=NimbusExperiment.Application.DESKTOP,
        )

        tasks.nimbus_push_experiment_to_kinto(
            settings.KINTO_COLLECTION_NIMBUS_DESKTOP, experiment.id)

        data = NimbusExperimentSerializer(experiment).data

        self.mock_kinto_client.create_record.assert_called_with(
            data=data,
            collection=settings.KINTO_COLLECTION_NIMBUS_DESKTOP,
            bucket=settings.KINTO_BUCKET_WORKSPACE,
            if_not_exists=True,
        )

        experiment = NimbusExperiment.objects.get(id=experiment.id)
        self.assertEqual(experiment.publish_status,
                         NimbusExperiment.PublishStatus.WAITING)
        self.assertTrue(
            experiment.changes.filter(
                old_publish_status=NimbusExperiment.PublishStatus.APPROVED,
                new_publish_status=NimbusExperiment.PublishStatus.WAITING,
                message=NimbusChangeLog.Messages.LAUNCHING_TO_KINTO,
            ).exists())
    def test_publishes_preview_experiments_and_unpublishes_non_preview_experiments(self):
        should_publish_experiment = NimbusExperimentFactory.create_with_lifecycle(
            NimbusExperimentFactory.Lifecycles.PREVIEW,
        )
        should_unpublish_experiment = NimbusExperimentFactory.create_with_lifecycle(
            NimbusExperimentFactory.Lifecycles.CREATED,
        )

        self.setup_kinto_get_main_records([should_unpublish_experiment.slug])

        tasks.nimbus_synchronize_preview_experiments_in_kinto()

        data = NimbusExperimentSerializer(should_publish_experiment).data

        self.mock_kinto_client.create_record.assert_called_with(
            data=data,
            collection=settings.KINTO_COLLECTION_NIMBUS_PREVIEW,
            bucket=settings.KINTO_BUCKET_WORKSPACE,
            if_not_exists=True,
        )
        self.mock_kinto_client.delete_record.assert_called_with(
            id=should_unpublish_experiment.slug,
            collection=settings.KINTO_COLLECTION_NIMBUS_PREVIEW,
            bucket=settings.KINTO_BUCKET_WORKSPACE,
        )
Exemple #3
0
    def test_updates_experiment_record_in_kinto(self):
        experiment = NimbusExperimentFactory.create_with_lifecycle(
            NimbusExperimentFactory.Lifecycles.PAUSING_APPROVE,
            application=NimbusExperiment.Application.DESKTOP,
        )

        tasks.nimbus_update_experiment_in_kinto(
            settings.KINTO_COLLECTION_NIMBUS_DESKTOP, experiment.id)

        data = NimbusExperimentSerializer(experiment).data

        self.mock_kinto_client.update_record.assert_called_with(
            data=data,
            collection=settings.KINTO_COLLECTION_NIMBUS_DESKTOP,
            bucket=settings.KINTO_BUCKET_WORKSPACE,
            if_match='"0"',
        )

        self.mock_kinto_client.patch_collection.assert_called_with(
            id=settings.KINTO_COLLECTION_NIMBUS_DESKTOP,
            data={"status": KINTO_REVIEW_STATUS},
            bucket=settings.KINTO_BUCKET_WORKSPACE,
        )

        experiment = NimbusExperiment.objects.get(id=experiment.id)
        self.assertEqual(experiment.publish_status,
                         NimbusExperiment.PublishStatus.WAITING)

        self.assertTrue(
            experiment.changes.filter(
                old_publish_status=NimbusExperiment.PublishStatus.APPROVED,
                new_publish_status=NimbusExperiment.PublishStatus.WAITING,
                message=NimbusChangeLog.Messages.UPDATING_IN_KINTO,
            ).exists())
Exemple #4
0
 def test_recipe_json_returns_serialized_data(self):
     user_email = "*****@*****.**"
     experiment = NimbusExperimentFactory.create_with_lifecycle(
         NimbusExperimentFactory.Lifecycles.CREATED
     )
     response = self.query(
         """
         query experimentBySlug($slug: String!) {
             experimentBySlug(slug: $slug) {
                 recipeJson
             }
         }
         """,
         variables={"slug": experiment.slug},
         headers={settings.OPENIDC_EMAIL_HEADER: user_email},
     )
     content = json.loads(response.content)
     self.assertEqual(response.status_code, 200)
     experiment_data = content["data"]["experimentBySlug"]
     self.assertEqual(
         experiment_data["recipeJson"],
         json.dumps(
             NimbusExperimentSerializer(experiment).data, indent=2, sort_keys=True
         ),
     )
Exemple #5
0
    def test_push_experiment_to_kinto_sends_fenix_experiment_data(self):
        experiment = NimbusExperimentFactory.create_with_status(
            NimbusExperiment.Status.DRAFT,
            application=NimbusExperiment.Application.FENIX,
            population_percent=Decimal("50.0"),
        )

        tasks.nimbus_push_experiment_to_kinto(experiment.id)

        self.assertEqual(experiment.bucket_range.start, 0)
        self.assertEqual(experiment.bucket_range.count, 5000)

        data = NimbusExperimentSerializer(experiment).data

        self.mock_kinto_client.create_record.assert_called_with(
            data=data,
            collection=settings.KINTO_COLLECTION_NIMBUS_MOBILE,
            bucket=settings.KINTO_BUCKET,
            if_not_exists=True,
        )

        self.assertTrue(
            NimbusChangeLog.objects.filter(
                experiment=experiment,
                changed_by__email=settings.KINTO_DEFAULT_CHANGELOG_USER,
                old_status=NimbusExperiment.Status.DRAFT,
                new_status=NimbusExperiment.Status.ACCEPTED,
            ).exists())
Exemple #6
0
    def create_with_lifecycle(cls,
                              lifecycle,
                              with_random_timespan=False,
                              **kwargs):
        experiment = cls.create(**kwargs)
        now = timezone.now() - datetime.timedelta(
            days=random.randint(100, 200))

        for state in lifecycle.value:
            experiment.apply_lifecycle_state(state)

            if (experiment.status == experiment.Status.LIVE
                    and experiment.status_next is None
                    and "published_dto" not in kwargs):
                experiment.published_dto = NimbusExperimentSerializer(
                    experiment).data

            experiment.save()

            if experiment.has_filter(
                    experiment.Filters.SHOULD_ALLOCATE_BUCKETS):
                experiment.allocate_bucket_range()

            change = generate_nimbus_changelog(
                experiment,
                experiment.owner,
                f"set lifecycle {lifecycle} state {state}",
            )

            if with_random_timespan:
                change.changed_on = now
                change.save()
                now += datetime.timedelta(days=random.randint(5, 20))

        return NimbusExperiment.objects.get(id=experiment.id)
Exemple #7
0
def nimbus_push_experiment_to_kinto(collection, experiment_id):
    """
    An invoked task that given a single experiment id, query it in the db, serialize it,
    and push its data to the configured collection. If it fails for any reason, log the
    error and reraise it so it will be forwarded to sentry.
    """

    metrics.incr("push_experiment_to_kinto.started")

    try:
        experiment = NimbusExperiment.objects.get(id=experiment_id)
        logger.info(f"Pushing {experiment.slug} to Kinto")

        kinto_client = KintoClient(collection)

        data = NimbusExperimentSerializer(experiment).data

        kinto_client.create_record(data)

        experiment.publish_status = NimbusExperiment.PublishStatus.WAITING
        experiment.save()

        generate_nimbus_changelog(
            experiment,
            get_kinto_user(),
            message=NimbusChangeLog.Messages.PUSHED_TO_KINTO,
        )

        logger.info(f"{experiment.slug} pushed to Kinto")
        metrics.incr("push_experiment_to_kinto.completed")
    except Exception as e:
        metrics.incr("push_experiment_to_kinto.failed")
        logger.info(
            f"Pushing experiment {experiment.slug} to Kinto failed: {e}")
        raise e
Exemple #8
0
def nimbus_update_experiment_in_kinto(collection, experiment_id):
    """
    An invoked task that given a single experiment id, reserializes
    and updates the record. If it fails for any reason, log the error and
    reraise it so it will be forwarded to sentry.
    """
    metrics.incr("update_experiment_in_kinto.started")

    try:
        experiment = NimbusExperiment.objects.get(id=experiment_id)
        logger.info(f"Updating {experiment.slug} in Kinto")

        kinto_client = KintoClient(collection)

        data = NimbusExperimentSerializer(experiment).data

        kinto_client.update_record(data)

        experiment.publish_status = NimbusExperiment.PublishStatus.WAITING
        experiment.save()

        generate_nimbus_changelog(
            experiment,
            get_kinto_user(),
            message=NimbusChangeLog.Messages.UPDATED_IN_KINTO,
        )

        logger.info(f"{experiment.slug} updated in Kinto")

        metrics.incr("update_experiment_in_kinto.completed")
    except Exception as e:
        metrics.incr("update_experiment_in_kinto.failed")
        logger.info(
            f"Updating experiment {experiment.slug} in Kinto failed: {e}")
        raise e
Exemple #9
0
 def test_serializer_with_branches_no_feature(self):
     experiment = NimbusExperimentFactory.create_with_lifecycle(
         NimbusExperimentFactory.Lifecycles.CREATED,
         feature_config=None,
     )
     experiment.save()
     serializer = NimbusExperimentSerializer(experiment)
     self.assertIsNone(serializer.data["branches"][0]["feature"]["featureId"])
Exemple #10
0
 def test_serializer_outputs_targeting(self):
     experiment = NimbusExperimentFactory.create_with_lifecycle(
         NimbusExperimentFactory.Lifecycles.LAUNCH_APPROVE,
         firefox_min_version=NimbusExperiment.Version.FIREFOX_83,
         targeting_config_slug=NimbusExperiment.TargetingConfig.ALL_ENGLISH,
         application=NimbusExperiment.Application.DESKTOP,
         channel=NimbusExperiment.Channel.NO_CHANNEL,
     )
     serializer = NimbusExperimentSerializer(experiment)
     self.assertEqual(serializer.data["targeting"], experiment.targeting)
     check_schema("experiments/NimbusExperiment", serializer.data)
 def test_serializers_with_feature_value_None(self):
     experiment = NimbusExperimentFactory.create_with_status(
         NimbusExperiment.Status.ACCEPTED,
         branches=[],
     )
     experiment.reference_branch = NimbusBranchFactory(
         experiment=experiment, feature_value=None)
     experiment.save()
     serializer = NimbusExperimentSerializer(experiment)
     self.assertIsNone(serializer.data["branches"][0]["feature"]["value"])
     check_schema("experiments/NimbusExperiment", serializer.data)
Exemple #12
0
    def test_serializer_outputs_empty_targeting(self):
        experiment = NimbusExperimentFactory.create_with_lifecycle(
            NimbusExperimentFactory.Lifecycles.LAUNCH_APPROVE,
            publish_status=NimbusExperiment.PublishStatus.APPROVED,
            targeting_config_slug=NimbusExperiment.TargetingConfig.NO_TARGETING,
            application=NimbusExperiment.Application.FENIX,
        )

        serializer = NimbusExperimentSerializer(experiment)
        self.assertEqual(serializer.data["targeting"], "true")
        check_schema("experiments/NimbusExperiment", serializer.data)
 def test_sets_application_channel_for_fenix_experiment(self):
     experiment = NimbusExperimentFactory.create_with_status(
         NimbusExperiment.Status.ACCEPTED,
         application=NimbusExperiment.Application.FENIX,
         channel=NimbusExperiment.Channel.FENIX_NIGHTLY,
     )
     serializer = NimbusExperimentSerializer(experiment)
     self.assertEqual(serializer.data["application"],
                      NimbusExperiment.Channel.FENIX_NIGHTLY)
     self.assertEqual(serializer.data["channel"],
                      NimbusExperiment.Channel.FENIX_NIGHTLY)
     check_schema("experiments/NimbusExperiment", serializer.data)
Exemple #14
0
 def test_serializers_with_missing_feature_value(self, application):
     experiment = NimbusExperimentFactory.create_with_lifecycle(
         NimbusExperimentFactory.Lifecycles.LAUNCH_APPROVE,
         application=application,
         branches=[],
     )
     experiment.reference_branch = NimbusBranchFactory(
         experiment=experiment, feature_value=None
     )
     experiment.save()
     serializer = NimbusExperimentSerializer(experiment)
     self.assertEqual(serializer.data["branches"][0]["feature"]["value"], {})
     check_schema("experiments/NimbusExperiment", serializer.data)
Exemple #15
0
    def test_get_nimbus_experiment_returns_expected_data(self):
        experiment = NimbusExperimentFactory.create_with_status(
            NimbusExperiment.Status.LIVE)

        response = self.client.get(
            reverse(
                "nimbus-experiment-rest-detail",
                kwargs={"slug": experiment.slug},
            ), )

        self.assertEqual(response.status_code, 200)
        json_data = json.loads(response.content)
        self.assertEqual(
            NimbusExperimentSerializer(experiment).data, json_data)
    def test_serializer_outputs_targeting_for_experiment_without_firefox_min_version(
        self, ):
        experiment = NimbusExperimentFactory.create_with_status(
            NimbusExperiment.Status.ACCEPTED,
            firefox_min_version=None,
            targeting_config_slug=NimbusExperiment.TargetingConfig.ALL_ENGLISH,
            channel=NimbusExperiment.Channel.DESKTOP_NIGHTLY,
        )

        serializer = NimbusExperimentSerializer(experiment)
        self.assertEqual(
            serializer.data["targeting"],
            ('browserSettings.update.channel == "nightly" '
             "&& localeLanguageCode == 'en' "
             "&& 'app.shield.optoutstudies.enabled'|preferenceValue"),
        )
        check_schema("experiments/NimbusExperiment", serializer.data)
    def test_serializer_outputs_targeting_for_experiment_without_channels(
            self):
        experiment = NimbusExperimentFactory.create_with_status(
            NimbusExperiment.Status.ACCEPTED,
            firefox_min_version=NimbusExperiment.Version.FIREFOX_80,
            targeting_config_slug=NimbusExperiment.TargetingConfig.ALL_ENGLISH,
            application=NimbusExperiment.Application.DESKTOP,
            channel=None,
        )

        serializer = NimbusExperimentSerializer(experiment)
        self.assertEqual(
            serializer.data["targeting"],
            ("version|versionCompare('80.!') >= 0 "
             "&& localeLanguageCode == 'en' "
             "&& 'app.shield.optoutstudies.enabled'|preferenceValue"),
        )
        check_schema("experiments/NimbusExperiment", serializer.data)
    def test_push_experiment_to_kinto_sends_fenix_experiment_data(self):
        experiment = NimbusExperimentFactory.create_with_lifecycle(
            NimbusExperimentFactory.Lifecycles.LAUNCH_APPROVE,
            application=NimbusExperiment.Application.DESKTOP,
        )

        tasks.nimbus_push_experiment_to_kinto(
            settings.KINTO_COLLECTION_NIMBUS_DESKTOP, experiment.id
        )

        data = NimbusExperimentSerializer(experiment).data

        self.mock_kinto_client.create_record.assert_called_with(
            data=data,
            collection=settings.KINTO_COLLECTION_NIMBUS_DESKTOP,
            bucket=settings.KINTO_BUCKET_WORKSPACE,
            if_not_exists=True,
        )
    def test_serializer_outputs_expected_schema_without_feature(self):
        experiment = NimbusExperimentFactory.create_with_status(
            NimbusExperiment.Status.ACCEPTED,
            feature_config=None,
        )
        serializer = NimbusExperimentSerializer(experiment)
        experiment_data = serializer.data.copy()
        branches_data = [dict(b) for b in experiment_data.pop("branches")]
        self.assertEqual(len(branches_data), 2)
        for branch in experiment.branches.all():
            self.assertIn(
                {
                    "slug": branch.slug,
                    "ratio": branch.ratio
                },
                branches_data,
            )

        check_schema("experiments/NimbusExperiment", serializer.data)
Exemple #20
0
def nimbus_push_experiment_to_kinto(experiment_id):
    """
    An invoked task that given a single experiment id, query it in the db, serialize it,
    and push its data to the configured collection. If it fails for any reason, log the
    error and reraise it so it will be forwarded to sentry.
    """

    metrics.incr("push_experiment_to_kinto.started")

    try:
        experiment = NimbusExperiment.objects.get(id=experiment_id)
        logger.info(f"Pushing {experiment} to Kinto")

        kinto_client = KintoClient(
            NimbusExperiment.KINTO_APPLICATION_COLLECTION[
                experiment.application])

        if not NimbusBucketRange.objects.filter(
                experiment=experiment).exists():
            NimbusIsolationGroup.request_isolation_group_buckets(
                experiment.slug,
                experiment,
                int(experiment.population_percent / Decimal("100.0") *
                    NimbusExperiment.BUCKET_TOTAL),
            )

        data = NimbusExperimentSerializer(experiment).data

        kinto_client.push_to_kinto(data)

        experiment.status = NimbusExperiment.Status.ACCEPTED
        experiment.save()

        generate_nimbus_changelog(experiment, get_kinto_user())

        logger.info(f"{experiment} pushed to Kinto")
        metrics.incr("push_experiment_to_kinto.completed")
    except Exception as e:
        metrics.incr("push_experiment_to_kinto.failed")
        logger.info(
            f"Pushing experiment id {experiment_id} to Kinto failed: {e}")
        raise e
Exemple #21
0
    def test_sets_app_id_name_channel_for_application(
        self,
        application,
        channel,
        expected_application,
        expected_appId,
        expected_appName,
    ):
        experiment = NimbusExperimentFactory.create_with_lifecycle(
            NimbusExperimentFactory.Lifecycles.LAUNCH_APPROVE,
            application=application,
            channel=channel,
        )

        serializer = NimbusExperimentSerializer(experiment)
        self.assertEqual(serializer.data["application"], expected_application)
        self.assertEqual(serializer.data["channel"], channel)
        self.assertEqual(serializer.data["appName"], expected_appName)
        self.assertEqual(serializer.data["appId"], expected_appId)
        check_schema("experiments/NimbusExperiment", serializer.data)
Exemple #22
0
def nimbus_synchronize_preview_experiments_in_kinto():
    """
    A scheduled task that pushes any experiments with status PREVIEW to the preview
    collection and removes any experiments not with status PREVIEW from the preview
    collection.
    """
    metrics.incr("nimbus_synchronize_preview_experiments_in_kinto.started")

    kinto_client = KintoClient(settings.KINTO_COLLECTION_NIMBUS_PREVIEW,
                               review=False)

    try:
        published_preview_slugs = kinto_client.get_main_records().keys()

        should_publish_experiments = NimbusExperiment.objects.filter(
            status=NimbusExperiment.Status.PREVIEW).exclude(
                slug__in=published_preview_slugs)

        for experiment in should_publish_experiments:
            data = NimbusExperimentSerializer(experiment).data
            kinto_client.create_record(data)
            logger.info(f"{experiment.slug} is being pushed to preview")

        should_unpublish_experiments = NimbusExperiment.objects.filter(
            slug__in=published_preview_slugs).exclude(
                status=NimbusExperiment.Status.PREVIEW)

        for experiment in should_unpublish_experiments:
            kinto_client.delete_record(experiment.slug)
            logger.info(f"{experiment.slug} is being removed from preview")

        metrics.incr(
            "nimbus_synchronize_preview_experiments_in_kinto.completed")

    except Exception as e:
        metrics.incr("nimbus_synchronize_preview_experiments_in_kinto.failed")
        logger.info(f"Synchronizing preview experiments failed: {e}")
        raise e
    def test_outputs_expected_schema_for_complete_experiment(self):
        application = NimbusExperiment.Application.DESKTOP
        feature_config = NimbusFeatureConfigFactory.create()
        project = ProjectFactory.create()
        primary_outcome = Outcomes.by_application(application)[0].slug
        secondary_outcome = Outcomes.by_application(application)[1].slug

        experiment = NimbusExperimentFactory.create_with_lifecycle(
            NimbusExperimentFactory.Lifecycles.ENDING_APPROVE_APPROVE,
            application=application,
            feature_config=feature_config,
            projects=[project],
            primary_outcomes=[primary_outcome],
            secondary_outcomes=[secondary_outcome],
        )
        data = dict(NimbusExperimentChangeLogSerializer(experiment).data)
        branches_data = [dict(b) for b in data.pop("branches")]
        control_branch_data = dict(data.pop("reference_branch"))
        locales_data = data.pop("locales")
        countries_data = data.pop("countries")
        feature_config_data = data.pop("feature_config")
        published_dto_data = data.pop("published_dto")

        self.assertEqual(
            data,
            {
                "application": experiment.application,
                "channel": experiment.channel,
                "firefox_min_version": experiment.firefox_min_version,
                "hypothesis": experiment.hypothesis,
                "is_paused": experiment.is_paused,
                "name": experiment.name,
                "owner": experiment.owner.email,
                "population_percent": str(experiment.population_percent),
                "primary_outcomes": [primary_outcome],
                "projects": [project.slug],
                "proposed_duration": experiment.proposed_duration,
                "proposed_enrollment": experiment.proposed_enrollment,
                "public_description": experiment.public_description,
                "publish_status": experiment.publish_status,
                "results_data": None,
                "risk_brand": experiment.risk_brand,
                "risk_mitigation_link": experiment.risk_mitigation_link,
                "risk_partner_related": experiment.risk_partner_related,
                "risk_revenue": experiment.risk_revenue,
                "secondary_outcomes": [secondary_outcome],
                "slug": experiment.slug,
                "status": experiment.status,
                "status_next": experiment.status_next,
                "targeting_config_slug": experiment.targeting_config_slug,
                "total_enrolled_clients": experiment.total_enrolled_clients,
            },
        )
        self.assertEqual(
            published_dto_data.keys(),
            dict(NimbusExperimentSerializer(experiment).data).keys(),
        )
        self.assertEqual(
            feature_config_data,
            {
                "name": feature_config.name,
                "slug": feature_config.slug,
                "description": feature_config.description,
                "application": feature_config.application,
                "owner_email": feature_config.owner_email,
                "schema": feature_config.schema,
            },
        )
        self.assertEqual(
            set(locales_data),
            set(experiment.locales.all().values_list("code", flat=True)),
        )
        self.assertEqual(
            set(countries_data),
            set(experiment.countries.all().values_list("code", flat=True)),
        )
        self.assertEqual(
            control_branch_data,
            {
                "description": experiment.reference_branch.description,
                "feature_enabled": experiment.reference_branch.feature_enabled,
                "feature_value": experiment.reference_branch.feature_value,
                "name": experiment.reference_branch.name,
                "ratio": experiment.reference_branch.ratio,
                "slug": experiment.reference_branch.slug,
            },
        )
        for branch in experiment.branches.all():
            self.assertIn(
                {
                    "description": branch.description,
                    "feature_enabled": branch.feature_enabled,
                    "feature_value": branch.feature_value,
                    "name": branch.name,
                    "ratio": branch.ratio,
                    "slug": branch.slug,
                },
                branches_data,
            )
    def test_serializer_outputs_expected_schema_with_feature(self):
        probe_set = NimbusProbeSetFactory.create()
        experiment = NimbusExperimentFactory.create_with_status(
            NimbusExperiment.Status.COMPLETE,
            application=NimbusExperiment.Application.DESKTOP,
            firefox_min_version=NimbusExperiment.Version.FIREFOX_80,
            targeting_config_slug=NimbusExperiment.TargetingConfig.ALL_ENGLISH,
            channel=NimbusExperiment.Channel.DESKTOP_NIGHTLY,
            probe_sets=[probe_set],
        )

        serializer = NimbusExperimentSerializer(experiment)
        all_experiment_data = serializer.data.copy()
        arguments_data = all_experiment_data.pop("arguments")
        for experiment_data in arguments_data, all_experiment_data:
            branches_data = [dict(b) for b in experiment_data.pop("branches")]

            self.assertEqual(
                experiment_data,
                {
                    "application":
                    experiment.application,
                    "channel":
                    experiment.channel,
                    "bucketConfig": {
                        "randomizationUnit":
                        (experiment.bucket_range.isolation_group.
                         randomization_unit),
                        "namespace":
                        experiment.bucket_range.isolation_group.namespace,
                        "start":
                        experiment.bucket_range.start,
                        "count":
                        experiment.bucket_range.count,
                        "total":
                        experiment.bucket_range.isolation_group.total,
                    },
                    # DRF manually replaces the isoformat suffix so we have to do the same
                    "endDate":
                    experiment.end_date.isoformat().replace("+00:00", "Z"),
                    "id":
                    experiment.slug,
                    "isEnrollmentPaused":
                    False,
                    "proposedDuration":
                    experiment.proposed_duration,
                    "proposedEnrollment":
                    experiment.proposed_enrollment,
                    "referenceBranch":
                    experiment.reference_branch.slug,
                    "schemaVersion":
                    settings.NIMBUS_SCHEMA_VERSION,
                    "slug":
                    experiment.slug,
                    # DRF manually replaces the isoformat suffix so we have to do the same
                    "startDate":
                    experiment.start_date.isoformat().replace("+00:00", "Z"),
                    "targeting":
                    ('browserSettings.update.channel == "nightly" '
                     "&& version|versionCompare('80.!') >= 0 "
                     "&& localeLanguageCode == 'en' "
                     "&& 'app.shield.optoutstudies.enabled'|preferenceValue"),
                    "userFacingDescription":
                    experiment.public_description,
                    "userFacingName":
                    experiment.name,
                    "probeSets": [probe_set.slug],
                },
            )
            self.assertEqual(len(branches_data), 2)
            for branch in experiment.branches.all():
                self.assertIn(
                    {
                        "slug": branch.slug,
                        "ratio": branch.ratio,
                        "feature": {
                            "featureId": experiment.feature_config.slug,
                            "enabled": branch.feature_enabled,
                            "value": json.loads(branch.feature_value),
                        },
                    },
                    branches_data,
                )

        check_schema("experiments/NimbusExperiment", serializer.data)
Exemple #25
0
 def resolve_recipe_json(self, info):
     return json.dumps(NimbusExperimentSerializer(self).data, indent=2, sort_keys=True)
Exemple #26
0
    def test_serializer_outputs_expected_schema_with_feature(self):
        experiment = NimbusExperimentFactory.create_with_lifecycle(
            NimbusExperimentFactory.Lifecycles.ENDING_APPROVE_APPROVE,
            application=NimbusExperiment.Application.DESKTOP,
            firefox_min_version=NimbusExperiment.Version.FIREFOX_83,
            targeting_config_slug=NimbusExperiment.TargetingConfig.ALL_ENGLISH,
            channel=NimbusExperiment.Channel.NIGHTLY,
            primary_outcomes=["foo", "bar", "baz"],
            secondary_outcomes=["quux", "xyzzy"],
        )

        serializer = NimbusExperimentSerializer(experiment)
        experiment_data = serializer.data.copy()
        bucket_data = dict(experiment_data.pop("bucketConfig"))
        branches_data = [dict(b) for b in experiment_data.pop("branches")]

        self.assertDictEqual(
            experiment_data,
            {
                "arguments": {},
                "application": "firefox-desktop",
                "appName": "firefox_desktop",
                "appId": "firefox-desktop",
                "channel": "nightly",
                # DRF manually replaces the isoformat suffix so we have to do the same
                "endDate": experiment.end_date.isoformat().replace("+00:00", "Z"),
                "id": experiment.slug,
                "isEnrollmentPaused": True,
                "proposedDuration": experiment.proposed_duration,
                "proposedEnrollment": experiment.proposed_enrollment,
                "referenceBranch": experiment.reference_branch.slug,
                "schemaVersion": settings.NIMBUS_SCHEMA_VERSION,
                "slug": experiment.slug,
                # DRF manually replaces the isoformat suffix so we have to do the same
                "startDate": experiment.start_date.isoformat().replace("+00:00", "Z"),
                "targeting": (
                    'browserSettings.update.channel == "nightly" '
                    "&& version|versionCompare('83.!') >= 0 "
                    "&& 'app.shield.optoutstudies.enabled'|preferenceValue "
                    "&& localeLanguageCode == 'en'"
                ),
                "userFacingDescription": experiment.public_description,
                "userFacingName": experiment.name,
                "probeSets": [],
                "outcomes": [
                    {"priority": "primary", "slug": "foo"},
                    {"priority": "primary", "slug": "bar"},
                    {"priority": "primary", "slug": "baz"},
                    {"priority": "secondary", "slug": "quux"},
                    {"priority": "secondary", "slug": "xyzzy"},
                ],
                "featureIds": [experiment.feature_config.slug],
            },
        )
        self.assertEqual(
            bucket_data,
            {
                "randomizationUnit": (
                    experiment.bucket_range.isolation_group.randomization_unit
                ),
                "namespace": experiment.bucket_range.isolation_group.namespace,
                "start": experiment.bucket_range.start,
                "count": experiment.bucket_range.count,
                "total": experiment.bucket_range.isolation_group.total,
            },
        )
        self.assertEqual(len(branches_data), 2)
        for branch in experiment.branches.all():
            self.assertIn(
                {
                    "slug": branch.slug,
                    "ratio": branch.ratio,
                    "feature": {
                        "featureId": experiment.feature_config.slug,
                        "enabled": branch.feature_enabled,
                        "value": json.loads(branch.feature_value),
                    },
                },
                branches_data,
            )

        check_schema("experiments/NimbusExperiment", serializer.data)