11/**
2- * Copyright 2016, 2018 Optimizely
2+ * Copyright 2016, 2018-2019 Optimizely
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1515 */
1616var audienceEvaluator = require ( './' ) ;
1717var chai = require ( 'chai' ) ;
18+ var sprintf = require ( 'sprintf-js' ) . sprintf ;
1819var conditionTreeEvaluator = require ( '../condition_tree_evaluator' ) ;
1920var customAttributeConditionEvaluator = require ( '../custom_attribute_condition_evaluator' ) ;
2021var sinon = require ( 'sinon' ) ;
21-
2222var assert = chai . assert ;
23+ var logger = require ( '../../plugins/logger' ) ;
24+ var enums = require ( '../../utils/enums' ) ;
25+ var LOG_LEVEL = enums . LOG_LEVEL ;
2326
2427var chromeUserAudience = {
2528 conditions : [ 'and' , {
@@ -52,12 +55,22 @@ var audiencesById = {
5255describe ( 'lib/core/audience_evaluator' , function ( ) {
5356 describe ( 'APIs' , function ( ) {
5457 describe ( 'evaluate' , function ( ) {
58+ var mockLogger = logger . createLogger ( { logLevel : LOG_LEVEL . INFO } ) ;
59+
60+ beforeEach ( function ( ) {
61+ sinon . stub ( mockLogger , 'log' ) ;
62+ } ) ;
63+
64+ afterEach ( function ( ) {
65+ mockLogger . log . restore ( ) ;
66+ } ) ;
67+
5568 it ( 'should return true if there are no audiences' , function ( ) {
56- assert . isTrue ( audienceEvaluator . evaluate ( [ ] , audiencesById , { } ) ) ;
69+ assert . isTrue ( audienceEvaluator . evaluate ( [ ] , audiencesById , { } , mockLogger ) ) ;
5770 } ) ;
5871
5972 it ( 'should return false if there are audiences but no attributes' , function ( ) {
60- assert . isFalse ( audienceEvaluator . evaluate ( [ '0' ] , audiencesById , { } ) ) ;
73+ assert . isFalse ( audienceEvaluator . evaluate ( [ '0' ] , audiencesById , { } , mockLogger ) ) ;
6174 } ) ;
6275
6376 it ( 'should return true if any of the audience conditions are met' , function ( ) {
@@ -74,9 +87,9 @@ describe('lib/core/audience_evaluator', function() {
7487 'device_model' : 'iphone' ,
7588 } ;
7689
77- assert . isTrue ( audienceEvaluator . evaluate ( [ '0' , '1' ] , audiencesById , iphoneUsers ) ) ;
78- assert . isTrue ( audienceEvaluator . evaluate ( [ '0' , '1' ] , audiencesById , chromeUsers ) ) ;
79- assert . isTrue ( audienceEvaluator . evaluate ( [ '0' , '1' ] , audiencesById , iphoneChromeUsers ) ) ;
90+ assert . isTrue ( audienceEvaluator . evaluate ( [ '0' , '1' ] , audiencesById , iphoneUsers , mockLogger ) ) ;
91+ assert . isTrue ( audienceEvaluator . evaluate ( [ '0' , '1' ] , audiencesById , chromeUsers , mockLogger ) ) ;
92+ assert . isTrue ( audienceEvaluator . evaluate ( [ '0' , '1' ] , audiencesById , iphoneChromeUsers , mockLogger ) ) ;
8093 } ) ;
8194
8295 it ( 'should return false if none of the audience conditions are met' , function ( ) {
@@ -93,21 +106,22 @@ describe('lib/core/audience_evaluator', function() {
93106 'device_model' : 'nexus5' ,
94107 } ;
95108
96- assert . isFalse ( audienceEvaluator . evaluate ( [ '0' , '1' ] , audiencesById , nexusUsers ) ) ;
97- assert . isFalse ( audienceEvaluator . evaluate ( [ '0' , '1' ] , audiencesById , safariUsers ) ) ;
98- assert . isFalse ( audienceEvaluator . evaluate ( [ '0' , '1' ] , audiencesById , nexusSafariUsers ) ) ;
109+ assert . isFalse ( audienceEvaluator . evaluate ( [ '0' , '1' ] , audiencesById , nexusUsers , mockLogger ) ) ;
110+ assert . isFalse ( audienceEvaluator . evaluate ( [ '0' , '1' ] , audiencesById , safariUsers , mockLogger ) ) ;
111+ assert . isFalse ( audienceEvaluator . evaluate ( [ '0' , '1' ] , audiencesById , nexusSafariUsers , mockLogger ) ) ;
99112 } ) ;
100113
101114 it ( 'should return true if no attributes are passed and the audience conditions evaluate to true in the absence of attributes' , function ( ) {
102- assert . isTrue ( audienceEvaluator . evaluate ( [ '2' ] , audiencesById ) ) ;
115+ assert . isTrue ( audienceEvaluator . evaluate ( [ '2' ] , audiencesById , null , mockLogger ) ) ;
103116 } ) ;
104117
105118 describe ( 'complex audience conditions' , function ( ) {
106119 it ( 'should return true if any of the audiences in an "OR" condition pass' , function ( ) {
107120 var result = audienceEvaluator . evaluate (
108121 [ 'or' , '0' , '1' ] ,
109122 audiencesById ,
110- { browser_type : 'chrome' }
123+ { browser_type : 'chrome' } ,
124+ mockLogger
111125 ) ;
112126 assert . isTrue ( result ) ;
113127 } ) ;
@@ -116,7 +130,8 @@ describe('lib/core/audience_evaluator', function() {
116130 var result = audienceEvaluator . evaluate (
117131 [ 'and' , '0' , '1' ] ,
118132 audiencesById ,
119- { browser_type : 'chrome' , device_model : 'iphone' }
133+ { browser_type : 'chrome' , device_model : 'iphone' } ,
134+ mockLogger
120135 ) ;
121136 assert . isTrue ( result ) ;
122137 } ) ;
@@ -125,7 +140,8 @@ describe('lib/core/audience_evaluator', function() {
125140 var result = audienceEvaluator . evaluate (
126141 [ 'not' , '1' ] ,
127142 audiencesById ,
128- { device_model : 'android' }
143+ { device_model : 'android' } ,
144+ mockLogger
129145 ) ;
130146 assert . isTrue ( result ) ;
131147 } ) ;
@@ -149,7 +165,8 @@ describe('lib/core/audience_evaluator', function() {
149165 var result = audienceEvaluator . evaluate (
150166 [ 'or' , '0' , '1' ] ,
151167 audiencesById ,
152- { browser_type : 'chrome' }
168+ { browser_type : 'chrome' } ,
169+ mockLogger
153170 ) ;
154171 assert . isTrue ( result ) ;
155172 } ) ;
@@ -159,7 +176,8 @@ describe('lib/core/audience_evaluator', function() {
159176 var result = audienceEvaluator . evaluate (
160177 [ 'or' , '0' , '1' ] ,
161178 audiencesById ,
162- { browser_type : 'safari' }
179+ { browser_type : 'safari' } ,
180+ mockLogger
163181 ) ;
164182 assert . isFalse ( result ) ;
165183 } ) ;
@@ -169,7 +187,8 @@ describe('lib/core/audience_evaluator', function() {
169187 var result = audienceEvaluator . evaluate (
170188 [ 'or' , '0' , '1' ] ,
171189 audiencesById ,
172- { state : 'California' }
190+ { state : 'California' } ,
191+ mockLogger
173192 ) ;
174193 assert . isFalse ( result ) ;
175194 } ) ;
@@ -180,10 +199,68 @@ describe('lib/core/audience_evaluator', function() {
180199 } ) ;
181200 customAttributeConditionEvaluator . evaluate . returns ( false ) ;
182201 var userAttributes = { device_model : 'android' } ;
183- var result = audienceEvaluator . evaluate ( [ 'or' , '1' ] , audiencesById , userAttributes ) ;
202+ var result = audienceEvaluator . evaluate ( [ 'or' , '1' ] , audiencesById , userAttributes , mockLogger ) ;
203+ sinon . assert . calledOnce ( customAttributeConditionEvaluator . evaluate ) ;
204+ sinon . assert . calledWithExactly ( customAttributeConditionEvaluator . evaluate , iphoneUserAudience . conditions [ 1 ] , userAttributes , mockLogger ) ;
205+ assert . isFalse ( result ) ;
206+ } ) ;
207+ } ) ;
208+
209+ describe ( 'Audience evaluation logging' , function ( ) {
210+ var sandbox = sinon . sandbox . create ( ) ;
211+
212+ beforeEach ( function ( ) {
213+ sandbox . stub ( conditionTreeEvaluator , 'evaluate' ) ;
214+ sandbox . stub ( customAttributeConditionEvaluator , 'evaluate' ) ;
215+ } ) ;
216+
217+ afterEach ( function ( ) {
218+ sandbox . restore ( ) ;
219+ } ) ;
220+
221+ it ( 'logs correctly when conditionTreeEvaluator.evaluate returns null' , function ( ) {
222+ conditionTreeEvaluator . evaluate . callsFake ( function ( conditions , leafEvaluator ) {
223+ return leafEvaluator ( conditions [ 1 ] ) ;
224+ } ) ;
225+ customAttributeConditionEvaluator . evaluate . returns ( null ) ;
226+ var userAttributes = { device_model : 5.5 } ;
227+ var result = audienceEvaluator . evaluate ( [ 'or' , '1' ] , audiencesById , userAttributes , mockLogger ) ;
228+ sinon . assert . calledOnce ( customAttributeConditionEvaluator . evaluate ) ;
229+ sinon . assert . calledWithExactly ( customAttributeConditionEvaluator . evaluate , iphoneUserAudience . conditions [ 1 ] , userAttributes , mockLogger ) ;
230+ assert . isFalse ( result ) ;
231+ assert . strictEqual ( 2 , mockLogger . log . callCount ) ;
232+ assert . strictEqual ( mockLogger . log . args [ 0 ] [ 1 ] , 'AUDIENCE_EVALUATOR: Starting to evaluate audience "1" with conditions: ["and",{"name":"device_model","value":"iphone","type":"custom_attribute"}].' ) ;
233+ assert . strictEqual ( mockLogger . log . args [ 1 ] [ 1 ] , 'AUDIENCE_EVALUATOR: Audience "1" evaluated to UNKNOWN.' ) ;
234+ } ) ;
235+
236+ it ( 'logs correctly when conditionTreeEvaluator.evaluate returns true' , function ( ) {
237+ conditionTreeEvaluator . evaluate . callsFake ( function ( conditions , leafEvaluator ) {
238+ return leafEvaluator ( conditions [ 1 ] ) ;
239+ } ) ;
240+ customAttributeConditionEvaluator . evaluate . returns ( true ) ;
241+ var userAttributes = { device_model : 'iphone' } ;
242+ var result = audienceEvaluator . evaluate ( [ 'or' , '1' ] , audiencesById , userAttributes , mockLogger ) ;
243+ sinon . assert . calledOnce ( customAttributeConditionEvaluator . evaluate ) ;
244+ sinon . assert . calledWithExactly ( customAttributeConditionEvaluator . evaluate , iphoneUserAudience . conditions [ 1 ] , userAttributes , mockLogger ) ;
245+ assert . isTrue ( result ) ;
246+ assert . strictEqual ( 2 , mockLogger . log . callCount ) ;
247+ assert . strictEqual ( mockLogger . log . args [ 0 ] [ 1 ] , 'AUDIENCE_EVALUATOR: Starting to evaluate audience "1" with conditions: ["and",{"name":"device_model","value":"iphone","type":"custom_attribute"}].' ) ;
248+ assert . strictEqual ( mockLogger . log . args [ 1 ] [ 1 ] , 'AUDIENCE_EVALUATOR: Audience "1" evaluated to TRUE.' ) ;
249+ } ) ;
250+
251+ it ( 'logs correctly when conditionTreeEvaluator.evaluate returns false' , function ( ) {
252+ conditionTreeEvaluator . evaluate . callsFake ( function ( conditions , leafEvaluator ) {
253+ return leafEvaluator ( conditions [ 1 ] ) ;
254+ } ) ;
255+ customAttributeConditionEvaluator . evaluate . returns ( false ) ;
256+ var userAttributes = { device_model : 'android' } ;
257+ var result = audienceEvaluator . evaluate ( [ 'or' , '1' ] , audiencesById , userAttributes , mockLogger ) ;
184258 sinon . assert . calledOnce ( customAttributeConditionEvaluator . evaluate ) ;
185- sinon . assert . calledWithExactly ( customAttributeConditionEvaluator . evaluate , iphoneUserAudience . conditions [ 1 ] , userAttributes ) ;
259+ sinon . assert . calledWithExactly ( customAttributeConditionEvaluator . evaluate , iphoneUserAudience . conditions [ 1 ] , userAttributes , mockLogger ) ;
186260 assert . isFalse ( result ) ;
261+ assert . strictEqual ( 2 , mockLogger . log . callCount ) ;
262+ assert . strictEqual ( mockLogger . log . args [ 0 ] [ 1 ] , 'AUDIENCE_EVALUATOR: Starting to evaluate audience "1" with conditions: ["and",{"name":"device_model","value":"iphone","type":"custom_attribute"}].' ) ;
263+ assert . strictEqual ( mockLogger . log . args [ 1 ] [ 1 ] , 'AUDIENCE_EVALUATOR: Audience "1" evaluated to FALSE.' ) ;
187264 } ) ;
188265 } ) ;
189266 } ) ;
0 commit comments