Skip to content

Commit 5e2137f

Browse files
committed
add: AIP#6 tests
1 parent d107bd5 commit 5e2137f

File tree

3 files changed

+262
-54
lines changed

3 files changed

+262
-54
lines changed

sentry/src/analytics_recorder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use crate::epoch;
2+
use crate::payout::get_payout;
23
use crate::Session;
34
use primitives::sentry::Event;
45
use primitives::sentry::{ChannelReport, PublisherReport};
56
use primitives::{BigNum, Channel};
67
use redis::aio::MultiplexedConnection;
78
use redis::pipe;
89
use slog::{error, Logger};
9-
use crate::payout::get_payout;
1010

1111
pub async fn record(
1212
mut conn: MultiplexedConnection,

sentry/src/middleware/auth.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ mod test {
134134
.expect("Handling the Request shouldn't have failed");
135135

136136
assert!(
137-
no_auth.extensions().get::<Session>().is_none(),
137+
no_auth.extensions().get::<AuthSession>().is_none(),
138138
"There shouldn't be a Session in the extensions"
139139
);
140140

@@ -147,7 +147,7 @@ mod test {
147147
.await
148148
.expect("Handling the Request shouldn't have failed");
149149
assert!(
150-
incorrect_auth.extensions().get::<Session>().is_none(),
150+
incorrect_auth.extensions().get::<AuthSession>().is_none(),
151151
"There shouldn't be a Session in the extensions"
152152
);
153153

sentry/src/payout.rs

Lines changed: 259 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use primitives::sentry::Event;
44
use primitives::ValidatorId;
55
use primitives::{BigNum, Channel};
66

7-
87
pub fn get_payout(channel: &Channel, event: &Event, session: &Session) -> BigNum {
98
match event {
109
Event::Impression { publisher, .. } | Event::Click { publisher, .. } => {
@@ -35,34 +34,30 @@ fn payout(
3534
min_price: BigNum,
3635
publisher: &ValidatorId,
3736
) -> 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
4450
.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);
6156

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)
6661
}
6762

6863
fn match_rule(
@@ -72,7 +67,7 @@ fn match_rule(
7267
uid: &ValidatorId,
7368
) -> bool {
7469
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()),
7671
None => true,
7772
};
7873

@@ -98,34 +93,247 @@ fn match_rule(
9893

9994
fn price_bounds(channel: &Channel, event: &Event) -> (BigNum, BigNum) {
10095
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+
_ => (
113101
channel.spec.min_per_impression.clone(),
114102
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+
),
126104
}
127105
}
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+
},
128114
_ => (Default::default(), Default::default()),
129115
}
130116
}
131117

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

Comments
 (0)