def test_serializer_bad_duplicate_variant_name(self): data = { "name": "rapid experiment", "objectives": "gotta go fast", "audience": "all_english", "features": ["picture_in_picture", "pinned_tabs"], "firefox_min_version": "80.0", "firefox_channel": Experiment.CHANNEL_RELEASE, "variants": [ { "name": "duplicate", "ratio": 50, "description": "a variant", "is_control": True, }, { "name": "duplicate", "ratio": 50, "description": "a variant", "is_control": True, }, ], } serializer = ExperimentRapidSerializer( data=data, context={"request": self.request} ) self.assertFalse(serializer.is_valid()) self.assertIn("variants", serializer.errors)
def test_serializer_bad_firefox_channel_value(self): data = { "name": "rapid experiment", "objectives": "gotta go fast", "audience": "all_english", "features": ["picture_in_picture", "pinned_tabs"], "firefox_min_version": FIREFOX_VERSION, "firefox_channel": "invalid channel", } serializer = ExperimentRapidSerializer( data=data, context={"request": self.request}) self.assertFalse(serializer.is_valid()) self.assertIn("firefox_channel", serializer.errors)
def test_serializer_bad_feature_value(self): data = { "name": "rapid experiment", "objectives": "gotta go fast", "audience": "all_english", "features": ["WRONG FEATURE 1", "WRONG FEATURE 2"], "firefox_min_version": FIREFOX_VERSION, "firefox_channel": Experiment.CHANNEL_RELEASE, } serializer = ExperimentRapidSerializer( data=data, context={"request": self.request}) self.assertFalse(serializer.is_valid()) self.assertIn("features", serializer.errors)
def test_serializer_bad_audience_value(self): data = { "name": "rapid experiment", "objectives": "gotta go fast", "audience": "WRONG AUDIENCE CHOICE", "features": ["picture_in_picture", "pinned_tabs"], "firefox_min_version": FIREFOX_VERSION, "firefox_channel": Experiment.CHANNEL_RELEASE, } serializer = ExperimentRapidSerializer( data=data, context={"request": self.request}) self.assertFalse(serializer.is_valid()) self.assertIn("audience", serializer.errors)
def test_serializer_required_fields(self): serializer = ExperimentRapidSerializer( data={}, context={"request": self.request}) self.assertFalse(serializer.is_valid()) self.assertEqual( set(serializer.errors.keys()), set([ "name", "objectives", "audience", "features", "firefox_min_version", "firefox_channel", ]), )
def test_serializer_returns_errors_for_non_alpha_numeric_name(self): data = { "name": "!!!!!!!!!!!!!!!", "objectives": "gotta go fast", "audience": "all_english", "features": ["picture_in_picture", "pinned_tabs"], "firefox_min_version": FIREFOX_VERSION, "firefox_channel": Experiment.CHANNEL_RELEASE, } serializer = ExperimentRapidSerializer( data=data, context={"request": self.request}) self.assertFalse(serializer.is_valid()) self.assertIn("Name needs to contain alphanumeric characters", serializer.errors["name"])
def test_serializer_update_experiment_does_not_throw_slug_err(self): experiment = ExperimentFactory.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": FIREFOX_VERSION, "firefox_channel": Experiment.CHANNEL_RELEASE, } serializer = ExperimentRapidSerializer( data=data, context={"request": self.request}, instance=experiment) self.assertTrue(serializer.is_valid())
def test_serializer_outputs_expected_schema(self): owner = UserFactory(email="*****@*****.**") experiment = ExperimentFactory.create( type=Experiment.TYPE_RAPID, rapid_type=Experiment.RAPID_AA_CFR, owner=owner, name="rapid experiment", slug="rapid-experiment", objectives="gotta go fast", audience="AUDIENCE 1", features=["FEATURE 1"], ) serializer = ExperimentRapidSerializer(experiment) self.assertDictEqual( serializer.data, { "owner": "*****@*****.**", "name": "rapid experiment", "slug": "rapid-experiment", "objectives": "gotta go fast", "audience": "AUDIENCE 1", "features": ["FEATURE 1"], }, )
def test_serializer_creates_changelog_for_updates(self): owner = UserFactory(email="*****@*****.**") experiment = ExperimentFactory.create_with_status( target_status=Experiment.STATUS_DRAFT, type=Experiment.TYPE_RAPID, rapid_type=Experiment.RAPID_AA_CFR, owner=owner, name="rapid experiment", slug="rapid-experiment", objectives="gotta go fast", public_description=Experiment.BUGZILLA_RAPID_EXPERIMENT_TEMPLATE, ) self.assertEqual(experiment.changes.count(), 1) data = { "name": "changing the name", "objectives": "changing objectives", } serializer = ExperimentRapidSerializer( instance=experiment, data=data, context={"request": self.request}) self.assertTrue(serializer.is_valid()) experiment = serializer.save() self.assertEqual(experiment.changes.count(), 2) changed_values = { "name": { "new_value": "changing the name", "old_value": "rapid experiment", "display_name": "Name", }, "objectives": { "new_value": "changing objectives", "old_value": "gotta go fast", "display_name": "Objectives", }, } self.assertTrue( experiment.changes.filter( old_status=Experiment.STATUS_DRAFT, new_status=Experiment.STATUS_DRAFT, changed_values=changed_values, ).exists())
def test_serializer_returns_error_for_non_unique_slug(self): ExperimentFactory.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": FIREFOX_VERSION, "firefox_channel": Experiment.CHANNEL_RELEASE, } 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 = ExperimentFactory.create_with_status( Experiment.STATUS_LIVE, type=Experiment.TYPE_RAPID, rapid_type=Experiment.RAPID_AA, owner=owner, name="rapid experiment", slug="rapid-experiment", objectives="gotta go fast", audience="all_english", features=["picture_in_picture"], firefox_channel=Experiment.CHANNEL_RELEASE, firefox_min_version=FIREFOX_VERSION, firefox_max_version=None, ) serializer = ExperimentRapidSerializer(experiment) 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": FIREFOX_VERSION, "firefox_channel": Experiment.CHANNEL_RELEASE, "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, }, )
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_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_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_get_detail_returns_data_for_rapid_experiment(self): user_email = "*****@*****.**" owner = UserFactory(email=user_email) experiment = ExperimentFactory.create( type=Experiment.TYPE_RAPID, owner=owner, name="rapid experiment", objectives="gotta go fast", ) 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_outputs_expected_schema_for_rejected_experiment(self): owner = UserFactory(email="*****@*****.**") experiment = ExperimentFactory.create_with_status( Experiment.STATUS_ACCEPTED, type=Experiment.TYPE_RAPID, rapid_type=Experiment.RAPID_AA, owner=owner, name="rapid experiment", slug="rapid-experiment", objectives="gotta go fast", audience="all_english", features=["picture_in_picture"], firefox_channel=Experiment.CHANNEL_RELEASE, firefox_min_version=FIREFOX_VERSION, firefox_max_version=None, ) ExperimentChangeLog.objects.create( old_status=Experiment.STATUS_ACCEPTED, new_status=Experiment.STATUS_REJECTED, message="It's no good", experiment=experiment, changed_by=owner, changed_on="2020-07-30T05:37:22.540985Z", ) experiment.status = Experiment.STATUS_REJECTED experiment.save() serializer = ExperimentRapidSerializer(experiment) 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": "Release", "firefox_min_version": FIREFOX_VERSION, "monitoring_dashboard_url": experiment.monitoring_dashboard_url, "name": "rapid experiment", "objectives": "gotta go fast", "owner": "*****@*****.**", "reject_feedback": { "changed_on": "2020-07-30T05:37:22.540985Z", "message": "It's no good", }, "slug": "rapid-experiment", "recipe_slug": experiment.recipe_slug, "status": Experiment.STATUS_REJECTED, }, )
def test_serializer_creates_experiment_and_sets_slug_and_changelog(self): data = { "name": "rapid experiment", "objectives": "gotta go fast", "audience": "AUDIENCE 1", "features": ["FEATURE 1", "FEATURE 2"], } serializer = ExperimentRapidSerializer( data=data, context={"request": self.request}) self.assertTrue(serializer.is_valid()) experiment = serializer.save() self.assertEqual(experiment.type, Experiment.TYPE_RAPID) self.assertEqual(experiment.rapid_type, Experiment.RAPID_AA_CFR) self.assertEqual(experiment.owner, self.user) self.assertEqual(experiment.name, "rapid experiment") self.assertEqual(experiment.slug, "rapid-experiment") self.assertEqual(experiment.objectives, "gotta go fast") self.assertEqual(experiment.public_description, Experiment.BUGZILLA_RAPID_EXPERIMENT_TEMPLATE) self.mock_tasks_serializer_create_bug.delay.assert_called() self.assertEqual(experiment.changes.count(), 1) changed_values = { "name": { "display_name": "Name", "new_value": "rapid experiment", "old_value": None, }, "objectives": { "display_name": "Objectives", "new_value": "gotta go fast", "old_value": None, }, "owner": { "display_name": "Owner", "new_value": self.user.id, "old_value": None, }, "type": { "display_name": "Type", "new_value": "rapid", "old_value": None }, "public_description": { "display_name": "Public Description", "new_value": Experiment.BUGZILLA_RAPID_EXPERIMENT_TEMPLATE, "old_value": None, }, "audience": { "display_name": "Audience", "new_value": "AUDIENCE 1", "old_value": None, }, "features": { "display_name": "Features", "new_value": ["FEATURE 1", "FEATURE 2"], "old_value": None, }, } self.assertTrue( experiment.changes.filter( old_status=None, new_status=Experiment.STATUS_DRAFT, changed_values=changed_values, ).exists())
def test_serializer_required_fields(self): serializer = ExperimentRapidSerializer( data={}, context={"request": self.request}) self.assertFalse(serializer.is_valid()) self.assertIn("name", serializer.errors) self.assertIn("objectives", serializer.errors)
def test_serializer_creates_experiment_and_sets_slug_and_changelog(self): data = { "name": "rapid experiment", "objectives": "gotta go fast", "audience": "all_english", "features": ["picture_in_picture", "pinned_tabs"], "firefox_min_version": FIREFOX_VERSION, "firefox_channel": Experiment.CHANNEL_RELEASE, } serializer = ExperimentRapidSerializer( data=data, context={"request": self.request}) self.assertTrue(serializer.is_valid()) experiment = serializer.save() # User input data self.assertEqual(experiment.type, Experiment.TYPE_RAPID) self.assertEqual(experiment.rapid_type, Experiment.RAPID_AA) self.assertEqual(experiment.owner, self.user) self.assertEqual(experiment.name, "rapid experiment") self.assertEqual(experiment.slug, "rapid-experiment") self.assertEqual(experiment.objectives, "gotta go fast") self.assertEqual(experiment.audience, "all_english") self.assertEqual(experiment.features, ["picture_in_picture", "pinned_tabs"]) self.assertEqual(experiment.firefox_min_version, FIREFOX_VERSION) self.assertEqual(experiment.firefox_channel, Experiment.CHANNEL_RELEASE) self.assertEqual(experiment.public_description, Experiment.BUGZILLA_RAPID_EXPERIMENT_TEMPLATE) # Preset data preset_data = NIMBUS_DATA["ExperimentDesignPresets"]["empty_aa"][ "preset"]["arguments"] self.assertEqual(experiment.firefox_channel, Experiment.CHANNEL_RELEASE) self.assertEqual(experiment.proposed_duration, preset_data["proposedDuration"]) self.assertEqual(experiment.proposed_enrollment, preset_data["proposedEnrollment"]) self.mock_tasks_serializer_create_bug.delay.assert_called() self.assertEqual(experiment.changes.count(), 1) changed_values = { "name": { "display_name": "Name", "new_value": "rapid experiment", "old_value": None, }, "objectives": { "display_name": "Objectives", "new_value": "gotta go fast", "old_value": None, }, "owner": { "display_name": "Owner", "new_value": self.user.id, "old_value": None, }, "type": { "display_name": "Type", "new_value": "rapid", "old_value": None }, "public_description": { "display_name": "Public Description", "new_value": Experiment.BUGZILLA_RAPID_EXPERIMENT_TEMPLATE, "old_value": None, }, "audience": { "display_name": "Audience", "new_value": "all_english", "old_value": None, }, "features": { "display_name": "Features", "new_value": ["picture_in_picture", "pinned_tabs"], "old_value": None, }, "firefox_min_version": { "display_name": "Firefox Min Version", "new_value": FIREFOX_VERSION, "old_value": None, }, "firefox_channel": { "display_name": "Firefox Channel", "new_value": Experiment.CHANNEL_RELEASE, "old_value": None, }, "proposed_duration": { "display_name": "Proposed Duration", "new_value": 28, "old_value": None, }, "proposed_enrollment": { "display_name": "Proposed Enrollment", "new_value": 7, "old_value": None, }, } changelog = experiment.changes.get(old_status=None, new_status=Experiment.STATUS_DRAFT) self.maxDiff = None self.assertEqual(changelog.changed_values, changed_values)
def test_serializer_creates_changelog_for_updates(self): owner = UserFactory(email="*****@*****.**") experiment = ExperimentFactory.create_with_status( target_status=Experiment.STATUS_DRAFT, type=Experiment.TYPE_RAPID, rapid_type=Experiment.RAPID_AA, 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=FIREFOX_VERSION, public_description=Experiment.BUGZILLA_RAPID_EXPERIMENT_TEMPLATE, ) 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": Experiment.VERSION_CHOICES[1][0], } serializer = ExperimentRapidSerializer( instance=experiment, data=data, context={"request": self.request}) self.assertTrue(serializer.is_valid()) experiment = serializer.save() self.assertEqual(experiment.changes.count(), 2) changed_values = { "name": { "display_name": "Name", "new_value": "changing the name", "old_value": "rapid experiment", }, "objectives": { "display_name": "Objectives", "new_value": "changing objectives", "old_value": "gotta go fast", }, "audience": { "display_name": "Audience", "new_value": "all_english", "old_value": "us_only", }, "features": { "display_name": "Features", "new_value": ["pinned_tabs"], "old_value": ["picture_in_picture", "pinned_tabs"], }, "firefox_min_version": { "display_name": "Firefox Min Version", "new_value": Experiment.VERSION_CHOICES[1][0], "old_value": FIREFOX_VERSION, }, "firefox_channel": { "display_name": "Firefox Channel", "new_value": Experiment.CHANNEL_NIGHTLY, "old_value": Experiment.CHANNEL_RELEASE, }, } self.assertTrue( experiment.changes.filter( old_status=Experiment.STATUS_DRAFT, new_status=Experiment.STATUS_DRAFT, changed_values=changed_values, ).exists())
def test_serializer_creates_experiment_and_sets_slug_and_changelog(self): data = { "name": "rapid experiment", "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.assertTrue(serializer.is_valid()) experiment = serializer.save() # Preset data preset_data = NIMBUS_DATA["ExperimentDesignPresets"]["empty_aa"]["preset"][ "arguments" ] # User input data self.assertEqual(experiment.type, Experiment.TYPE_RAPID) self.assertEqual(experiment.rapid_type, Experiment.RAPID_AA) self.assertEqual(experiment.owner, self.user) self.assertEqual(experiment.name, "rapid experiment") self.assertEqual(experiment.slug, "rapid-experiment") self.assertEqual(experiment.objectives, "gotta go fast") self.assertEqual(experiment.audience, "all_english") self.assertEqual(experiment.features, ["picture_in_picture", "pinned_tabs"]) self.assertEqual(experiment.firefox_min_version, "80.0") self.assertEqual(experiment.firefox_channel, Experiment.CHANNEL_RELEASE) self.assertEqual( experiment.public_description, Experiment.BUGZILLA_RAPID_EXPERIMENT_TEMPLATE ) self.assertEqual(experiment.firefox_channel, Experiment.CHANNEL_RELEASE) self.assertEqual(experiment.proposed_duration, preset_data["proposedDuration"]) self.assertEqual( experiment.proposed_enrollment, preset_data["proposedEnrollment"] ) self.assertEqual(experiment.variants.count(), 2) self.assertTrue(experiment.variants.filter(**self.variants_data[0]).exists()) self.assertTrue(experiment.variants.filter(**self.variants_data[1]).exists()) self.mock_tasks_serializer_create_bug.delay.assert_called() self.assertEqual(experiment.changes.count(), 1) changelog = experiment.changes.get( old_status=None, new_status=Experiment.STATUS_DRAFT ) self.assertEqual(changelog.changed_by, self.request.user) self.assertEqual( set(changelog.changed_values.keys()), set( [ "audience", "features", "firefox_channel", "firefox_min_version", "name", "objectives", "owner", "proposed_duration", "proposed_enrollment", "public_description", "type", "variants", ] ), )
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", ] ), )