@@ -4,7 +4,6 @@ use primitives::sentry::Event;
4
4
use primitives:: ValidatorId ;
5
5
use primitives:: { BigNum , Channel } ;
6
6
7
-
8
7
pub fn get_payout ( channel : & Channel , event : & Event , session : & Session ) -> BigNum {
9
8
match event {
10
9
Event :: Impression { publisher, .. } | Event :: Click { publisher, .. } => {
@@ -35,34 +34,30 @@ fn payout(
35
34
min_price : BigNum ,
36
35
publisher : & ValidatorId ,
37
36
) -> BigNum {
38
- if !rules. is_empty ( ) {
39
- let matching_rules: Vec < & PriceMultiplicationRules > = rules
40
- . iter ( )
41
- . filter ( |& rule| match_rule ( rule, & event, & session, & publisher) )
42
- . collect ( ) ;
43
- let fixed_amount_rule = matching_rules
37
+ let matching_rules: Vec < & PriceMultiplicationRules > = rules
38
+ . iter ( )
39
+ . filter ( |& rule| match_rule ( rule, & event, & session, & publisher) )
40
+ . collect ( ) ;
41
+ let fixed_amount_rule = matching_rules
42
+ . iter ( )
43
+ . find ( |& rule| rule. amount . is_some ( ) )
44
+ . map ( |& rule| rule. amount . as_ref ( ) . expect ( "should have value" ) ) ;
45
+ let price_by_rules = if let Some ( amount) = fixed_amount_rule {
46
+ amount. clone ( )
47
+ } else {
48
+ let exponent: f64 = 10.0 ;
49
+ let multiplier = rules
44
50
. iter ( )
45
- . find ( |& rule| rule. amount . is_some ( ) )
46
- . map ( |& rule| rule. amount . as_ref ( ) . expect ( "should have value" ) ) ;
47
- let price_by_rules = if let Some ( amount) = fixed_amount_rule {
48
- amount. clone ( )
49
- } else {
50
- let exponent: f64 = 10.0 ;
51
- let multiplier = rules
52
- . iter ( )
53
- . filter ( |& rule| rule. multiplier . is_some ( ) )
54
- . map ( |rule| rule. multiplier . expect ( "should have value" ) )
55
- . fold ( 1.0 , |result, i| result * i) ;
56
- let value: u64 = ( multiplier * exponent. powi ( 18 ) ) as u64 ;
57
- let result = min_price * BigNum :: from ( value) ;
58
-
59
- result / BigNum :: from ( 10u64 . pow ( 18 ) )
60
- } ;
51
+ . filter ( |& rule| rule. multiplier . is_some ( ) )
52
+ . map ( |rule| rule. multiplier . expect ( "should have value" ) )
53
+ . fold ( 1.0 , |result, i| result * i) ;
54
+ let value: u64 = ( multiplier * exponent. powi ( 18 ) ) as u64 ;
55
+ let result = min_price * BigNum :: from ( value) ;
61
56
62
- max_price . min ( price_by_rules )
63
- } else {
64
- min_price
65
- }
57
+ result / BigNum :: from ( 10u64 . pow ( 18 ) )
58
+ } ;
59
+
60
+ max_price . min ( price_by_rules )
66
61
}
67
62
68
63
fn match_rule (
@@ -72,7 +67,7 @@ fn match_rule(
72
67
uid : & ValidatorId ,
73
68
) -> bool {
74
69
let ev_type = match & rule. ev_type {
75
- Some ( event_types) => event_types. contains ( ev_type) ,
70
+ Some ( event_types) => event_types. contains ( & ev_type. to_string ( ) ) ,
76
71
None => true ,
77
72
} ;
78
73
@@ -98,34 +93,247 @@ fn match_rule(
98
93
99
94
fn price_bounds ( channel : & Channel , event : & Event ) -> ( BigNum , BigNum ) {
100
95
let pricing_bounds = channel. spec . pricing_bounds . as_ref ( ) ;
101
- match event {
102
- Event :: Impression { .. } => {
103
- if let Some ( pricing_bounds) = pricing_bounds {
104
- match pricing_bounds. impression . as_ref ( ) {
105
- Some ( pricing) => ( pricing. min . clone ( ) , pricing. max . clone ( ) ) ,
106
- _ => (
107
- channel. spec . min_per_impression . clone ( ) ,
108
- channel. spec . max_per_impression . clone ( ) ,
109
- ) ,
110
- }
111
- } else {
112
- (
96
+ match ( event, pricing_bounds) {
97
+ ( Event :: Impression { .. } , Some ( pricing_bounds) ) => {
98
+ match pricing_bounds. impression . as_ref ( ) {
99
+ Some ( pricing) => ( pricing. min . clone ( ) , pricing. max . clone ( ) ) ,
100
+ _ => (
113
101
channel. spec . min_per_impression . clone ( ) ,
114
102
channel. spec . max_per_impression . clone ( ) ,
115
- )
116
- }
117
- }
118
- Event :: Click { .. } => {
119
- if let Some ( pricing_bounds) = pricing_bounds {
120
- match pricing_bounds. click . as_ref ( ) {
121
- Some ( pricing) => ( pricing. min . clone ( ) , pricing. max . clone ( ) ) ,
122
- _ => ( Default :: default ( ) , Default :: default ( ) ) ,
123
- }
124
- } else {
125
- ( Default :: default ( ) , Default :: default ( ) )
103
+ ) ,
126
104
}
127
105
}
106
+ ( Event :: Impression { .. } , None ) => (
107
+ channel. spec . min_per_impression . clone ( ) ,
108
+ channel. spec . max_per_impression . clone ( ) ,
109
+ ) ,
110
+ ( Event :: Click { .. } , Some ( pricing_bounds) ) => match pricing_bounds. click . as_ref ( ) {
111
+ Some ( pricing) => ( pricing. min . clone ( ) , pricing. max . clone ( ) ) ,
112
+ _ => ( Default :: default ( ) , Default :: default ( ) ) ,
113
+ } ,
128
114
_ => ( Default :: default ( ) , Default :: default ( ) ) ,
129
115
}
130
116
}
131
117
118
+ #[ cfg( test) ]
119
+ mod tests {
120
+ use super :: * ;
121
+ use primitives:: channel:: { Pricing , PricingBounds } ;
122
+ use primitives:: util:: tests:: prep_db:: { AUTH , DUMMY_CHANNEL , IDS } ;
123
+
124
+ #[ test]
125
+ fn test_plain_events ( ) {
126
+ let mut channel: Channel = DUMMY_CHANNEL . clone ( ) ;
127
+ channel. spec . pricing_bounds = Some ( PricingBounds {
128
+ click : Some ( Pricing {
129
+ min : BigNum :: from ( 23 ) ,
130
+ max : BigNum :: from ( 100 ) ,
131
+ } ) ,
132
+ impression : None ,
133
+ } ) ;
134
+
135
+ let cases: Vec < ( Event , BigNum , String ) > = vec ! [
136
+ (
137
+ Event :: Impression {
138
+ publisher: IDS [ "publisher" ] . clone( ) ,
139
+ ad_slot: None ,
140
+ ad_unit: None ,
141
+ referrer: None ,
142
+ } ,
143
+ BigNum :: from( 1 ) ,
144
+ "pricingBounds: impression event" . to_string( ) ,
145
+ ) ,
146
+ (
147
+ Event :: Click {
148
+ publisher: IDS [ "publisher" ] . clone( ) ,
149
+ ad_slot: None ,
150
+ ad_unit: None ,
151
+ referrer: None ,
152
+ } ,
153
+ BigNum :: from( 23 ) ,
154
+ "pricingBounds: click event" . to_string( ) ,
155
+ ) ,
156
+ (
157
+ Event :: Close { } ,
158
+ BigNum :: from( 0 ) ,
159
+ "pricingBounds: close event" . to_string( ) ,
160
+ ) ,
161
+ ] ;
162
+
163
+ let session = Session {
164
+ ip : None ,
165
+ country : None ,
166
+ referrer_header : None ,
167
+ os : None ,
168
+ } ;
169
+
170
+ cases. iter ( ) . for_each ( |case| {
171
+ let ( event, expected_result, message) = case;
172
+ let payout = get_payout ( & channel, & event, & session) ;
173
+ // println!("payout {:?}", payout.to_f64());
174
+ assert ! ( & payout == expected_result, message. clone( ) ) ;
175
+ } )
176
+ }
177
+
178
+ #[ test]
179
+ fn test_fixed_amount_price_rule_event ( ) {
180
+ let mut channel: Channel = DUMMY_CHANNEL . clone ( ) ;
181
+ channel. spec . pricing_bounds = Some ( PricingBounds {
182
+ click : Some ( Pricing {
183
+ min : BigNum :: from ( 23 ) ,
184
+ max : BigNum :: from ( 100 ) ,
185
+ } ) ,
186
+ impression : None ,
187
+ } ) ;
188
+ channel. spec . price_multiplication_rules = vec ! [ PriceMultiplicationRules {
189
+ multiplier: None ,
190
+ amount: Some ( BigNum :: from( 10 ) ) ,
191
+ os_type: None ,
192
+ ev_type: Some ( vec![ "CLICK" . to_string( ) ] ) ,
193
+ publisher: None ,
194
+ country: Some ( vec![ "us" . to_string( ) ] ) ,
195
+ } ] ;
196
+
197
+ let cases: Vec < ( Event , BigNum , String ) > = vec ! [
198
+ (
199
+ Event :: Impression {
200
+ publisher: IDS [ "publisher" ] . clone( ) ,
201
+ ad_slot: None ,
202
+ ad_unit: None ,
203
+ referrer: None ,
204
+ } ,
205
+ BigNum :: from( 1 ) ,
206
+ "fixedAmount: impression" . to_string( ) ,
207
+ ) ,
208
+ (
209
+ Event :: Click {
210
+ publisher: IDS [ "publisher" ] . clone( ) ,
211
+ ad_slot: None ,
212
+ ad_unit: None ,
213
+ referrer: None ,
214
+ } ,
215
+ BigNum :: from( 10 ) ,
216
+ "fixedAmount (country, publisher): click" . to_string( ) ,
217
+ ) ,
218
+ ] ;
219
+
220
+ let session = Session {
221
+ ip : None ,
222
+ country : Some ( "us" . to_string ( ) ) ,
223
+ referrer_header : None ,
224
+ os : None ,
225
+ } ;
226
+
227
+ cases. iter ( ) . for_each ( |case| {
228
+ let ( event, expected_result, message) = case;
229
+ let payout = get_payout ( & channel, & event, & session) ;
230
+ assert ! ( & payout == expected_result, message. clone( ) ) ;
231
+ } )
232
+ }
233
+
234
+ #[ test]
235
+ fn test_fixed_amount_exceed_rule_event ( ) {
236
+ let mut channel: Channel = DUMMY_CHANNEL . clone ( ) ;
237
+ channel. spec . pricing_bounds = Some ( PricingBounds {
238
+ click : Some ( Pricing {
239
+ min : BigNum :: from ( 23 ) ,
240
+ max : BigNum :: from ( 100 ) ,
241
+ } ) ,
242
+ impression : None ,
243
+ } ) ;
244
+ channel. spec . price_multiplication_rules = vec ! [ PriceMultiplicationRules {
245
+ multiplier: None ,
246
+ amount: Some ( BigNum :: from( 1000 ) ) ,
247
+ os_type: None ,
248
+ ev_type: None ,
249
+ publisher: None ,
250
+ country: None ,
251
+ } ] ;
252
+
253
+ let cases: Vec < ( Event , BigNum , String ) > = vec ! [
254
+ (
255
+ Event :: Impression {
256
+ publisher: IDS [ "publisher" ] . clone( ) ,
257
+ ad_slot: None ,
258
+ ad_unit: None ,
259
+ referrer: None ,
260
+ } ,
261
+ BigNum :: from( 10 ) ,
262
+ "fixedAmount (all): price should not exceed maxPerImpressionPrice" . to_string( ) ,
263
+ ) ,
264
+ (
265
+ Event :: Click {
266
+ publisher: IDS [ "publisher" ] . clone( ) ,
267
+ ad_slot: None ,
268
+ ad_unit: None ,
269
+ referrer: None ,
270
+ } ,
271
+ BigNum :: from( 100 ) ,
272
+ "fixedAmount (all): price should not exceed event pricingBound max" . to_string( ) ,
273
+ ) ,
274
+ ] ;
275
+
276
+ let session = Session {
277
+ ip : None ,
278
+ country : Some ( "us" . to_string ( ) ) ,
279
+ referrer_header : None ,
280
+ os : None ,
281
+ } ;
282
+
283
+ cases. iter ( ) . for_each ( |case| {
284
+ let ( event, expected_result, message) = case;
285
+ let payout = get_payout ( & channel, & event, & session) ;
286
+ assert ! ( & payout == expected_result, message. clone( ) ) ;
287
+ } )
288
+ }
289
+
290
+ #[ test]
291
+ fn test_pick_first_fixed_amount_rule_event ( ) {
292
+ let mut channel: Channel = DUMMY_CHANNEL . clone ( ) ;
293
+ channel. spec . pricing_bounds = Some ( PricingBounds {
294
+ click : Some ( Pricing {
295
+ min : BigNum :: from ( 23 ) ,
296
+ max : BigNum :: from ( 100 ) ,
297
+ } ) ,
298
+ impression : None ,
299
+ } ) ;
300
+ channel. spec . price_multiplication_rules = vec ! [
301
+ PriceMultiplicationRules {
302
+ multiplier: None ,
303
+ amount: Some ( BigNum :: from( 10 ) ) ,
304
+ os_type: None ,
305
+ ev_type: Some ( vec![ "CLICK" . to_string( ) ] ) ,
306
+ publisher: None ,
307
+ country: Some ( vec![ "us" . to_string( ) ] ) ,
308
+ } ,
309
+ PriceMultiplicationRules {
310
+ multiplier: None ,
311
+ amount: Some ( BigNum :: from( 12 ) ) ,
312
+ os_type: None ,
313
+ ev_type: Some ( vec![ "CLICK" . to_string( ) ] ) ,
314
+ publisher: Some ( vec![ IDS [ "publisher" ] . clone( ) ] ) ,
315
+ country: Some ( vec![ "us" . to_string( ) ] ) ,
316
+ } ,
317
+ ] ;
318
+
319
+ let session = Session {
320
+ ip : None ,
321
+ country : Some ( "us" . to_string ( ) ) ,
322
+ referrer_header : None ,
323
+ os : None ,
324
+ } ;
325
+
326
+ let event = Event :: Click {
327
+ publisher : IDS [ "publisher" ] . clone ( ) ,
328
+ ad_slot : None ,
329
+ ad_unit : None ,
330
+ referrer : None ,
331
+ } ;
332
+
333
+ let payout = get_payout ( & channel, & event, & session) ;
334
+ assert ! (
335
+ payout == BigNum :: from( 10 ) ,
336
+ "fixedAmount (country, pulisher): should choose first fixedAmount rule"
337
+ ) ;
338
+ }
339
+ }
0 commit comments