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)
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, }, )
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"], )
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)
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())
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)
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)
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)
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())
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)
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)
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)
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 }, ], )
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", ] ), )