public void TestIsUserInExperimentAudienceNoMatch() { var userAttributes = new UserAttributes { { "device_type", "Android" }, { "location", "San Francisco" } }; Assert.IsFalse(ExperimentUtils.IsUserInExperiment(Config, Config.GetExperimentFromKey("test_experiment"), null, Logger)); }
/// <summary> /// Create impression event to be sent to the logging endpoint. /// </summary> /// <param name="config">ProjectConfig Configuration for the project</param> /// <param name="experiment">Experiment being activated</param> /// <param name="variationId">Variation Id</param> /// <param name="userId">User Id</param> /// <param name="userAttributes">associative array of attributes for the user</param> /// <returns>LogEvent object to be sent to dispatcher</returns> public virtual LogEvent CreateImpressionEvent(ProjectConfig config, Experiment experiment, string variationId, string userId, UserAttributes userAttributes) { var commonParams = GetCommonParams(config, userId, userAttributes ?? new UserAttributes()); var impressionOnlyParams = GetImpressionParams(experiment, variationId); var impressionParams = GetImpressionOrConversionParamsWithCommonParams(commonParams, new object[] { impressionOnlyParams }); return(new LogEvent(IMPRESSION_ENDPOINT, impressionParams, HTTP_VERB, HTTP_HEADERS)); }
public CanonicalEvent(string experimentId, string variationId, string eventName, string visitorId, UserAttributes attributes, EventTags tags) { ExperimentId = experimentId; VariationId = variationId; EventName = eventName; VisitorId = visitorId; Attributes = attributes ?? new UserAttributes(); Tags = tags ?? new EventTags(); }
public void TestDoesUserMeetAudienceConditionsReturnsTrueIfAllAudiencesInANDConditionPass() { var experiment = Config.GetExperimentFromKey("audience_combinations_experiment"); var userAttributes = new UserAttributes { { "house", "Gryffindor" }, { "favorite_ice_cream", "walls" } }; Assert.True(ExperimentUtils.DoesUserMeetAudienceConditions(Config, experiment, userAttributes, "experiment", experiment.Key, Logger).ResultObject); }
public void TestEvaluateConditionsDoNotMatch() { var userAttributes = new UserAttributes { { "device_type", "iPhone" }, { "location", "San Francisco" }, { "browser", "Firefox" } }; Assert.IsFalse(ConditionEvaluator.Evaluate(Conditions, userAttributes)); }
/// <summary> /// Returns copy of UserAttributes associated with UserContext. /// </summary> /// <returns>copy of UserAttributes.</returns> public UserAttributes GetAttributes() { UserAttributes copiedAttributes = null; lock (mutex) { copiedAttributes = new UserAttributes(Attributes); } return(copiedAttributes); }
public void TestIsUserInExperimentReturnsTrueIfAllAudiencesInANDConditionPass() { var experiment = Config.GetExperimentFromKey("audience_combinations_experiment"); var userAttributes = new UserAttributes { { "house", "Gryffindor" }, { "favorite_ice_cream", "walls" } }; Assert.True(ExperimentUtils.IsUserInExperiment(Config, experiment, userAttributes, Logger)); }
public void TestDoesUserMeetAudienceConditionsReturnsTrueIfAudienceInNOTConditionDoesNotPass() { var experiment = Config.GetExperimentFromKey("feat_with_var_test"); experiment.AudienceIds = new string[] { "3468206645" }; var userAttributes = new UserAttributes { { "browser_type", "Safari" } }; Assert.True(ExperimentUtils.DoesUserMeetAudienceConditions(Config, experiment, userAttributes, "experiment", experiment.Key, Logger)); }
public void TestIsUserInExperimentReturnsFalseIfAudienceInNOTConditionGetsPassed() { var experiment = Config.GetExperimentFromKey("feat_with_var_test"); experiment.AudienceIds = new string[] { "3468206645" }; var userAttributes = new UserAttributes { { "browser_type", "Chrome" } }; Assert.False(ExperimentUtils.IsUserInExperiment(Config, experiment, userAttributes, Logger)); }
public void OptimizelyUserContextWithAttributes() { var attributes = new UserAttributes() { { "house", "GRYFFINDOR" } }; OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, attributes, ErrorHandlerMock.Object, LoggerMock.Object); Assert.AreEqual(user.GetOptimizely(), Optimizely); Assert.AreEqual(user.GetUserId(), UserID); Assert.AreEqual(user.GetAttributes(), attributes); }
public void TestDoesUserMeetAudienceConditionsReturnsFalseIfAudienceInNOTConditionGetsPassed() { var experiment = Config.GetExperimentFromKey("feat_with_var_test"); experiment.AudienceIds = new string[] { "3468206645" }; var userAttributes = new UserAttributes { { "browser_type", "Chrome" } }; Assert.False(ExperimentUtils.DoesUserMeetAudienceConditions(Config, experiment, userAttributes, "experiment", experiment.Key, Logger).ResultObject); }
/// <summary> /// Sends conversion event to Optimizely. /// </summary> /// <param name="eventKey">Event key representing the event which needs to be recorded</param> /// <param name="userId">ID for user</param> /// <param name="userAttributes">Attributes of the user</param> /// <param name="eventTags">eventTags array Hash representing metadata associated with the event.</param> public void Track(string eventKey, string userId, UserAttributes userAttributes = null, EventTags eventTags = null) { if (!IsValid) { Logger.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'track'."); return; } var inputValues = new Dictionary <string, string> { { USER_ID, userId }, { EVENT_KEY, eventKey } }; if (!ValidateStringInputs(inputValues)) { return; } var eevent = Config.GetEvent(eventKey); if (eevent.Key == null) { Logger.Log(LogLevel.INFO, string.Format("Not tracking user {0} for event {1}.", userId, eventKey)); return; } if (eventTags != null) { eventTags = eventTags.FilterNullValues(Logger); } var conversionEvent = EventBuilder.CreateConversionEvent(Config, eventKey, userId, userAttributes, eventTags); Logger.Log(LogLevel.INFO, string.Format("Tracking event {0} for user {1}.", eventKey, userId)); Logger.Log(LogLevel.DEBUG, string.Format("Dispatching conversion event to URL {0} with params {1}.", conversionEvent.Url, conversionEvent.GetParamsAsJson())); try { EventDispatcher.DispatchEvent(conversionEvent); } catch (Exception exception) { Logger.Log(LogLevel.ERROR, string.Format("Unable to dispatch conversion event. Error {0}", exception.Message)); } NotificationCenter.SendNotifications(NotificationCenter.NotificationType.Track, eventKey, userId, userAttributes, eventTags, conversionEvent); }
/// <summary> /// Get Bucketing ID from user attributes. /// </summary> /// <param name = "userId" >User Identifier</param> /// <param name = "filteredAttributes" >The user's attributes.</param> /// <returns>User ID if bucketing Id is not provided in user attributes, bucketing Id otherwise.</returns> private string GetBucketingId(string userId, UserAttributes filteredAttributes) { string bucketingId = userId; // If the bucketing ID key is defined in attributes, then use that in place of the userID for the murmur hash key if (filteredAttributes != null && filteredAttributes.ContainsKey(RESERVED_ATTRIBUTE_KEY_BUCKETING_ID)) { bucketingId = filteredAttributes[RESERVED_ATTRIBUTE_KEY_BUCKETING_ID]; Logger.Log(LogLevel.DEBUG, string.Format("Setting the bucketing ID to \"{0}\"", bucketingId)); } return(bucketingId); }
public void TestIsUserAttributeValidWithInvalidValues() { var invalidUserAttributes = new UserAttributes { { "objects", new object() }, { "arrays", new string[] { "a", "b", "c" } } }; foreach (var attribute in invalidUserAttributes) { Assert.False(Validator.IsUserAttributeValid(attribute)); } }
/// <summary> /// Create ImpressionEvent instance from ProjectConfig /// </summary> /// <param name="projectConfig">The ProjectConfig entity</param> /// <param name="activatedExperiment">The Experiment entity</param> /// <param name="variationId">The variation Id</param> /// <param name="userId">The user Id</param> /// <param name="userAttributes">The user's attributes</param> /// <returns>ImpressionEvent instance</returns> public static ImpressionEvent CreateImpressionEvent(ProjectConfig projectConfig, Experiment activatedExperiment, string variationId, string userId, UserAttributes userAttributes, string flagKey, string ruleType, bool enabled = false) { Variation variation = projectConfig.GetVariationFromId(activatedExperiment?.Key, variationId); return(CreateImpressionEvent(projectConfig, activatedExperiment, variation, userId, userAttributes, flagKey, ruleType, enabled)); }
public void DispatchEvent(LogEvent actualLogEvent) { Visitor[] visitors = null; if (actualLogEvent.Params.ContainsKey("visitors")) { JArray jArray = (JArray)actualLogEvent.Params["visitors"]; visitors = jArray.ToObject <Visitor[]>(); } if (visitors == null) { return; } foreach (var visitor in visitors) { foreach (var snapshot in visitor.Snapshots) { var decisions = snapshot.Decisions ?? new Decision[1] { new Decision() }; foreach (var decision in decisions) { foreach (var @event in snapshot.Events) { var userAttributes = new UserAttributes(); foreach (var attribute in visitor.Attributes.Where(attr => !attr.Key.StartsWith(DatafileProjectConfig.RESERVED_ATTRIBUTE_PREFIX))) { userAttributes.Add(attribute.Key, attribute.Value); } ActualEvents.Add(new CanonicalEvent(decision.ExperimentId, decision.VariationId, @event.Key, visitor.VisitorId, userAttributes, @event.EventTags)); } } } } try { CountdownEvent?.Signal(); } catch (ObjectDisposedException) { Logger.Log(LogLevel.ERROR, "The CountdownEvent instance has already been disposed."); } catch (InvalidOperationException) { Logger.Log(LogLevel.ERROR, "The CountdownEvent instance has already been set."); } }
public void TestIsUserAttributeValidWithEmptyKeyOrValue() { var validUserAttributes = new UserAttributes { { "", "Android" }, { "integer", 0 }, { "string", string.Empty } }; foreach (var attribute in validUserAttributes) { Assert.True(Validator.IsUserAttributeValid(attribute)); } }
private static async void GetCommand(GetOptions authOptions, TaskCompletionSource <object> taskCompletionSource) { try { if (authOptions.User) { User user = await KitsuAPI.GetUserByIDAsync(authOptions.Id, kitsuSession); UserAttributes userAttributes = user.Attributes.As <UserAttributes>(); Console.WriteLine("-"); Console.WriteLine("Name: {0}", userAttributes.Name); Console.WriteLine("ID: {0}", user.Id); Console.WriteLine("--About:"); Console.WriteLine(userAttributes.About); Console.WriteLine("--"); Console.WriteLine("-"); } else { IEntity media = null; if (authOptions.Anime) { media = await KitsuAPI.GetAnimeByIDAsync(authOptions.Id, kitsuSession); } else if (authOptions.Manga) { media = await KitsuAPI.GetMangaByIDAsync(authOptions.Id, kitsuSession); } else if (authOptions.Drama) { } if (media != null) { PrintMedia(media); } } taskCompletionSource.SetResult(null); } catch (Exception ex) { Console.Error.WriteLine("Error occurred."); Console.Error.WriteLine(ex.ToString()); taskCompletionSource.SetException(ex); } }
public void DecideAllTwoFlag() { var flagKey1 = "multi_variate_feature"; var flagKey2 = "string_single_variable_feature"; var flagKeys = new string[] { flagKey1, flagKey2 }; var variablesExpected1 = Optimizely.GetAllFeatureVariables(flagKey1, UserID); var variablesExpected2 = Optimizely.GetAllFeatureVariables(flagKey2, UserID); var user = Optimizely.CreateUserContext(UserID); user.SetAttribute("browser_type", "chrome"); // Mocking objects. NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <UserAttributes>(), It.IsAny <Dictionary <string, object> >())); Optimizely.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback); var decisions = user.DecideForKeys(flagKeys); var userAttributes = new UserAttributes { { "browser_type", "chrome" } }; Assert.True(decisions.Count == 2); NotificationCallbackMock.Verify(nc => nc.TestDecisionCallback(DecisionNotificationTypes.FLAG, UserID, userAttributes, It.IsAny <Dictionary <string, object> >()), Times.Exactly(2)); OptimizelyDecision expDecision1 = new OptimizelyDecision( "Gred", false, variablesExpected1, "test_experiment_multivariate", flagKey1, user, new string[0]); Assert.IsTrue(TestData.CompareObjects(decisions[flagKey1], expDecision1)); OptimizelyDecision expDecision2 = new OptimizelyDecision( "control", true, variablesExpected2, "test_experiment_with_feature_rollout", flagKey2, user, new string[0]); Assert.IsTrue(TestData.CompareObjects(decisions[flagKey2], expDecision2)); }
/// <summary> /// Determine whether a feature is enabled. /// Send an impression event if the user is bucketed into an experiment using the feature. /// </summary> /// <param name="experimentKey">The experiment key</param> /// <param name="userId">The user ID</param> /// <param name="userAttributes">The user's attributes.</param> /// <returns>True if feature is enabled, false or null otherwise</returns> public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttributes userAttributes = null) { if (string.IsNullOrEmpty(userId)) { Logger.Log(LogLevel.ERROR, "User ID must not be empty."); return(false); } if (string.IsNullOrEmpty(featureKey)) { Logger.Log(LogLevel.ERROR, "Feature flag key must not be empty."); return(false); } var featureFlag = Config.GetFeatureFlagFromKey(featureKey); if (string.IsNullOrEmpty(featureFlag.Key)) { return(false); } if (!Validator.IsFeatureFlagValid(Config, featureFlag)) { return(false); } var decision = DecisionService.GetVariationForFeature(featureFlag, userId, userAttributes); if (decision != null) { if (decision.Source == FeatureDecision.DECISION_SOURCE_EXPERIMENT) { SendImpressionEvent(decision.Experiment, decision.Variation, userId, userAttributes); } else { Logger.Log(LogLevel.INFO, $@"The user ""{userId}"" is not being experimented on feature ""{featureKey}""."); } if (decision.Variation.IsFeatureEnabled) { Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is enabled for user ""{userId}""."); return(true); } } Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is not enabled for user ""{userId}""."); return(false); }
public void TestIsUserAttributeValidWithValidValues() { var userAttributes = new UserAttributes { { "device_type", "Android" }, { "is_firefox", true }, { "num_users", 15 }, { "pi_value", 3.14 } }; foreach (var attribute in userAttributes) { Assert.True(Validator.IsUserAttributeValid(attribute)); } }
public void TestEvaluateWithDifferentTypedAttributes() { var userAttributes = new UserAttributes { { "browser_type", "firefox" }, { "is_registered_user", false }, { "distance_gt", 15 }, { "pi_value", 3.14 }, }; Assert.That(ExactStrCondition.Evaluate(null, userAttributes, Logger), Is.True); Assert.That(ExactBoolCondition.Evaluate(null, userAttributes, Logger), Is.True); Assert.That(GTCondition.Evaluate(null, userAttributes, Logger), Is.True); Assert.That(ExactDecimalCondition.Evaluate(null, userAttributes, Logger), Is.True); }
public void TestTrackEventWithAudienceConditions() { var OptimizelyWithTypedAudiences = new Optimizely(TestData.TypedAudienceDatafile, EventDispatcherMock.Object, LoggerMock.Object, ErrorHandlerMock.Object); var userAttributes = new UserAttributes { { "house", "Gryffindor" }, { "should_do_it", false } }; var user = OptimizelyWithTypedAudiences.CreateUserContext(UserID, userAttributes); // Should be excluded as exact match boolean audience with id '3468206643' does not match so the overall conditions fail. user.TrackEvent("user_signed_up"); EventDispatcherMock.Verify(dispatcher => dispatcher.DispatchEvent(It.IsAny <LogEvent>()), Times.Once); }
public void SetAttributeOverride() { var attributes = new UserAttributes() { { "house", "GRYFFINDOR" } }; OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, attributes, ErrorHandlerMock.Object, LoggerMock.Object); user.SetAttribute("k1", "v1"); user.SetAttribute("house", "v2"); var newAttributes = user.GetAttributes(); Assert.AreEqual(newAttributes["k1"], "v1"); Assert.AreEqual(newAttributes["house"], "v2"); }
public void TestGetVariationForFeatureRolloutWhenUserIsNeitherBucketedInTheTargetingRuleNorToEveryoneElseRule() { var featureFlag = ProjectConfig.GetFeatureFlagFromKey("boolean_single_variable_feature"); var rolloutId = featureFlag.RolloutId; var rollout = ProjectConfig.GetRolloutFromId(rolloutId); var userAttributes = new UserAttributes { { "browser_type", "chrome" } }; BucketerMock.Setup(bm => bm.Bucket(It.IsAny <ProjectConfig>(), It.IsAny <Experiment>(), It.IsAny <string>(), It.IsAny <string>())).Returns <Variation>(null); var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object); var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, "user_1", userAttributes); Assert.IsNull(actualDecision); }
/// <summary> /// Sends conversion event to Optimizely. /// </summary> /// <param name="eventKey">Event key representing the event which needs to be recorded</param> /// <param name="userId">ID for user</param> /// <param name="userAttributes">Attributes of the user</param> /// <param name="eventTags">eventTags array Hash representing metadata associated with the event.</param> public void Track(string eventKey, string userId, UserAttributes userAttributes = null, EventTags eventTags = null) { var config = ProjectConfigManager?.GetConfig(); if (config == null) { Logger.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'Track'."); return; } var inputValues = new Dictionary <string, string> { { USER_ID, userId }, { EVENT_KEY, eventKey } }; if (!ValidateStringInputs(inputValues)) { return; } var eevent = config.GetEvent(eventKey); if (eevent.Key == null) { Logger.Log(LogLevel.INFO, string.Format("Not tracking user {0} for event {1}.", userId, eventKey)); return; } if (eventTags != null) { eventTags = eventTags.FilterNullValues(Logger); } var userEvent = UserEventFactory.CreateConversionEvent(config, eventKey, userId, userAttributes, eventTags); EventProcessor.Process(userEvent); Logger.Log(LogLevel.INFO, string.Format("Tracking event {0} for user {1}.", eventKey, userId)); if (NotificationCenter.GetNotificationCount(NotificationCenter.NotificationType.Track) > 0) { var conversionEvent = EventFactory.CreateLogEvent(userEvent, Logger); NotificationCenter.SendNotifications(NotificationCenter.NotificationType.Track, eventKey, userId, userAttributes, eventTags, conversionEvent); } }
public void TestIsUserInExperimentReturnsTrueIfAnyAudienceInORConditionPass() { var experiment = Config.GetExperimentFromKey("feat_with_var_test"); var userAttributes = new UserAttributes { { "house", "Gryffindor" }, { "lasers", 50 }, { "should_do_it", false } }; Assert.True(ExperimentUtils.IsUserInExperiment(Config, experiment, userAttributes, Logger)); LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, @"Evaluating audiences for experiment ""feat_with_var_test"": [""3468206642"",""3988293898"",""3988293899"",""3468206646"",""3468206647"",""3468206644"",""3468206643""]"), Times.Once); LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, @"Starting to evaluate audience ""3468206642"" with conditions: [""and"", [""or"", [""or"", {""name"": ""house"", ""type"": ""custom_attribute"", ""value"": ""Gryffindor""}]]]"), Times.Once); LoggerMock.Verify(l => l.Log(LogLevel.INFO, @"Audience ""3468206642"" evaluated to TRUE"), Times.Once); LoggerMock.Verify(l => l.Log(LogLevel.INFO, @"Audiences for experiment ""feat_with_var_test"" collectively evaluated to TRUE"), Times.Once); }
public void TestGetVariationForFeatureExperimentGivenNonMutexGroupAndUserIsBucketed() { var experiment = ProjectConfig.GetExperimentFromKey("test_experiment_multivariate"); var variation = ProjectConfig.GetVariationFromId("test_experiment_multivariate", "122231"); var expectedDecision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_EXPERIMENT); var userAttributes = new UserAttributes(); DecisionServiceMock.Setup(ds => ds.GetVariation(ProjectConfig.GetExperimentFromKey("test_experiment_multivariate"), "user1", userAttributes)).Returns(variation); var featureFlag = ProjectConfig.GetFeatureFlagFromKey("multi_variate_feature"); var decision = DecisionServiceMock.Object.GetVariationForFeatureExperiment(featureFlag, "user1", userAttributes); Assert.IsTrue(TestData.CompareObjects(expectedDecision, decision)); LoggerMock.Verify(l => l.Log(LogLevel.INFO, "The user \"user1\" is bucketed into experiment \"test_experiment_multivariate\" of feature \"multi_variate_feature\".")); }
public async Task ClientUpdateUser() { var user = $"{RandomString(12)}@supabase.io"; await client.SignUp(user, password); var attributes = new UserAttributes { Data = new Dictionary <string, object> { { "hello", "world" } } }; await client.Update(attributes); Assert.AreEqual(user, client.CurrentUser.Email); Assert.IsNotNull(client.CurrentUser.UserMetadata); }
public void TestGetVariationForFeatureExperimentGivenMutexGroupAndUserIsBucketed() { var mutexExperiment = ProjectConfig.GetExperimentFromKey("group_experiment_1"); var variation = mutexExperiment.Variations[0]; var userAttributes = new UserAttributes(); var expectedDecision = new FeatureDecision(mutexExperiment, variation, FeatureDecision.DECISION_SOURCE_EXPERIMENT); DecisionServiceMock.Setup(ds => ds.GetVariation(ProjectConfig.GetExperimentFromKey("group_experiment_1"), "user1", userAttributes)).Returns(variation); var featureFlag = ProjectConfig.GetFeatureFlagFromKey("boolean_feature"); var actualDecision = DecisionServiceMock.Object.GetVariationForFeatureExperiment(featureFlag, "user1", userAttributes); Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision)); LoggerMock.Verify(l => l.Log(LogLevel.INFO, "The user \"user1\" is bucketed into experiment \"group_experiment_1\" of feature \"boolean_feature\".")); }