Skip to content

Commit 70de853

Browse files
committed
sentry - routes - units-for-slot test setup
- sentry - platform - make the URL pub(crate)
1 parent 7682d40 commit 70de853

File tree

3 files changed

+314
-5
lines changed

3 files changed

+314
-5
lines changed

sentry/src/platform.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub type Result<T> = std::result::Result<T, Error>;
1010
#[derive(Debug, Clone)]
1111
/// The `PlatformApi` is cheap to clone
1212
pub struct PlatformApi {
13-
platform_url: ApiUrl,
13+
pub(crate) platform_url: ApiUrl,
1414
client: Client,
1515
}
1616

sentry/src/routes/units_for_slot.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ use crate::{
4040
pub(crate) static CLOUDFLARE_IPCOUNTRY_HEADER: Lazy<HeaderName> =
4141
Lazy::new(|| HeaderName::from_static("cf-ipcountry"));
4242

43-
// #[cfg(test)]
44-
// #[path = "units_for_slot_test.rs"]
45-
// pub mod test;
43+
#[cfg(test)]
44+
#[path = "./units_for_slot_test.rs"]
45+
pub mod test;
4646

4747
#[derive(Debug, Error)]
4848
#[error(transparent)]
@@ -171,7 +171,6 @@ where
171171
Ok(Json(response))
172172
}
173173

174-
// TODO: Use error instead of std::error::Error
175174
async fn get_campaigns(
176175
pool: DbPool,
177176
campaign_remaining: CampaignRemaining,
@@ -182,6 +181,7 @@ async fn get_campaigns(
182181
// 1. Fetch active Campaigns: (postgres)
183182
// Creator = publisher_id
184183
// if deposit asset > 0 => filter by deposit_asset
184+
// TODO: limit the amount of passable deposit assets to avoid the max query size!
185185
let active_campaigns = units_for_slot_get_campaigns(
186186
&pool,
187187
deposit_assets.as_ref(),
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
use std::iter::Iterator;
2+
3+
use adapter::{
4+
ethereum::test_util::{GANACHE_INFO_1, GANACHE_INFO_1337},
5+
primitives::Deposit,
6+
};
7+
use axum::http::HeaderValue;
8+
use chrono::{TimeZone, Utc};
9+
use primitives::{
10+
platform::{AdSlotResponse, Website},
11+
sentry::{
12+
campaign_create::CreateCampaign,
13+
units_for_slot::response::{
14+
AdUnit as ResponseAdUnit, Campaign as ResponseCampaign, UnitsWithPrice,
15+
},
16+
},
17+
targeting::{Function, Rules, Value},
18+
test_util::{CAMPAIGNS, DUMMY_AD_UNITS, DUMMY_IPFS, IDS, PUBLISHER},
19+
unified_num::FromWhole,
20+
AdSlot,
21+
};
22+
23+
use super::*;
24+
use crate::{
25+
routes::campaign::create_campaign,
26+
test_util::{setup_dummy_app, ApplicationGuard},
27+
Auth,
28+
};
29+
30+
// use url::Url;
31+
use wiremock::{
32+
matchers::{method, path},
33+
Mock, MockServer, ResponseTemplate,
34+
};
35+
36+
// User Agent OS: Linux (only in `woothee`)
37+
// User Agent Browser Family: Firefox
38+
const LINUX_FIREFOX_USER_AGENT: &str =
39+
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0";
40+
// uses two-letter country codes: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
41+
const BG_CLOUDFLARE_IPCOUNTRY: &str = "BG";
42+
43+
fn get_categories_rules(categories: &[&str]) -> Rules {
44+
let get_rule = Function::new_get("adSlot.categories");
45+
let categories_array = Value::Array(categories.iter().map(|s| Value::new_string(s)).collect());
46+
let intersects_rule = Function::new_intersects(get_rule, categories_array);
47+
48+
Rules(vec![Function::new_only_show_if(intersects_rule).into()])
49+
}
50+
51+
async fn setup_mocked_platform_dummy_app() -> (MockServer, ApplicationGuard) {
52+
// For mocking the `get_market_demand_resp` call
53+
let mock_server = MockServer::start().await;
54+
55+
let platform_url = mock_server.uri().parse().unwrap();
56+
57+
let mut app_guard = setup_dummy_app().await;
58+
app_guard.app.platform_api.platform_url = platform_url;
59+
60+
(mock_server, app_guard)
61+
}
62+
63+
#[tokio::test]
64+
async fn test_targeting_input() {
65+
let (mock_server, app_guard) = setup_mocked_platform_dummy_app().await;
66+
let app = Arc::new(app_guard.app);
67+
68+
let fallback_unit = DUMMY_AD_UNITS[0].clone();
69+
70+
let ad_slot = AdSlot {
71+
ipfs: DUMMY_IPFS[0],
72+
ad_type: "legacy_250x250".to_string(),
73+
archived: false,
74+
created: Utc.ymd(2019, 7, 29).and_hms(7, 0, 0),
75+
description: Some("Test slot for running integration tests".to_string()),
76+
fallback_unit: None,
77+
min_per_impression: Some(
78+
[
79+
(
80+
GANACHE_INFO_1.tokens["Mocked TOKEN 1"].address,
81+
UnifiedNum::from_whole(0.0007),
82+
),
83+
(
84+
GANACHE_INFO_1337.tokens["Mocked TOKEN 1337"].address,
85+
UnifiedNum::from_whole(0.001),
86+
),
87+
]
88+
.into_iter()
89+
.collect(),
90+
),
91+
modified: Some(Utc.ymd(2019, 7, 29).and_hms(7, 0, 0)),
92+
owner: IDS[&PUBLISHER],
93+
title: Some("Test slot 1".to_string()),
94+
website: Some("https://adex.network".to_string()),
95+
rules: Rules::default(),
96+
};
97+
assert_ne!(fallback_unit.ipfs, ad_slot.ipfs);
98+
99+
// we only match campaign 2 from Chain id 1 due to it's impression min price
100+
let campaigns = {
101+
let campaign_0 = {
102+
let deposit = Deposit {
103+
total: UnifiedNum::from_whole(200_000)
104+
.to_precision(CAMPAIGNS[0].token.precision.into()),
105+
};
106+
107+
app.adapter.client.set_deposit(
108+
&CAMPAIGNS[0].of_channel(),
109+
CAMPAIGNS[0].context.creator,
110+
deposit,
111+
);
112+
113+
CAMPAIGNS[0] /* .context */
114+
.clone()
115+
};
116+
117+
let campaign_1 = {
118+
let deposit = Deposit {
119+
total: UnifiedNum::from_whole(200_000)
120+
.to_precision(CAMPAIGNS[1].token.precision.into()),
121+
};
122+
123+
app.adapter.client.set_deposit(
124+
&CAMPAIGNS[1].of_channel(),
125+
CAMPAIGNS[1].context.creator,
126+
deposit,
127+
);
128+
129+
CAMPAIGNS[1] /* .context */
130+
.clone()
131+
};
132+
133+
let matching_campaign_2 = {
134+
let deposit = Deposit {
135+
total: UnifiedNum::from_whole(100_000_000)
136+
.to_precision(CAMPAIGNS[2].token.precision.into()),
137+
};
138+
139+
app.adapter.client.set_deposit(
140+
&CAMPAIGNS[2].of_channel(),
141+
CAMPAIGNS[2].context.creator,
142+
deposit,
143+
);
144+
145+
let min_impression_price =
146+
Function::new_only_show_if(Function::new_get("eventMinPrice"));
147+
148+
let mut campaign_2 = CAMPAIGNS[2] /* .context */
149+
.clone();
150+
campaign_2.context.targeting_rules = Rules(vec![min_impression_price.into()]);
151+
152+
campaign_2
153+
};
154+
155+
[campaign_0, campaign_1, matching_campaign_2]
156+
};
157+
158+
// campaign creation
159+
// Instead of inserting manually redis & postgres data for the campaign, just use the route
160+
// to create the campaigns for the test in the DB
161+
{
162+
// campaign 0
163+
{
164+
let created_campaign = create_campaign(
165+
Json(CreateCampaign::from_campaign(campaigns[0].context.clone())),
166+
Extension(Auth {
167+
era: 0,
168+
uid: campaigns[0].context.creator.into(),
169+
chain: campaigns[0].chain.clone(),
170+
}),
171+
Extension(app.clone()),
172+
)
173+
.await
174+
.expect("Should create campaign");
175+
assert_eq!(&created_campaign.0, &campaigns[0].context);
176+
}
177+
178+
// campaign 1
179+
{
180+
let created_campaign = create_campaign(
181+
Json(CreateCampaign::from_campaign(campaigns[1].context.clone())),
182+
Extension(Auth {
183+
era: 0,
184+
uid: campaigns[1].context.creator.into(),
185+
chain: campaigns[1].chain.clone(),
186+
}),
187+
Extension(app.clone()),
188+
)
189+
.await
190+
.expect("Should create campaign");
191+
assert_eq!(&created_campaign.0, &campaigns[1].context);
192+
}
193+
194+
// matching campaign 2
195+
{
196+
let created_campaign = create_campaign(
197+
Json(CreateCampaign::from_campaign(campaigns[2].context.clone())),
198+
Extension(Auth {
199+
era: 0,
200+
uid: campaigns[2].context.creator.into(),
201+
chain: campaigns[2].chain.clone(),
202+
}),
203+
Extension(app.clone()),
204+
)
205+
.await
206+
.expect("Should create campaign");
207+
assert_eq!(&created_campaign.0, &campaigns[2].context);
208+
}
209+
}
210+
211+
let platform_response = AdSlotResponse {
212+
slot: ad_slot.clone(),
213+
fallback: Some(fallback_unit.clone()),
214+
website: Some(Website {
215+
categories: [
216+
"IAB3".to_string(),
217+
"IAB13-7".to_string(),
218+
"IAB5".to_string(),
219+
]
220+
.into_iter()
221+
.collect(),
222+
accepted_referrers: vec![],
223+
}),
224+
};
225+
226+
let deposit_assets = [
227+
GANACHE_INFO_1.tokens["Mocked TOKEN 1"].address,
228+
GANACHE_INFO_1337.tokens["Mocked TOKEN 1337"].address,
229+
]
230+
.into_iter()
231+
.collect();
232+
233+
let headers = [(
234+
CLOUDFLARE_IPCOUNTRY_HEADER.clone(),
235+
BG_CLOUDFLARE_IPCOUNTRY
236+
.parse::<HeaderValue>()
237+
.expect("Should parse header value"),
238+
)]
239+
.into_iter()
240+
.collect();
241+
242+
let _mock_guard = Mock::given(method("GET"))
243+
.and(path(format!("/slot/{}", ad_slot.ipfs)))
244+
.respond_with(ResponseTemplate::new(200).set_body_json(platform_response))
245+
.expect(1)
246+
.named("platform_slot")
247+
.mount(&mock_server).await;
248+
249+
let response = get_units_for_slot(
250+
Extension(app.clone()),
251+
Path(ad_slot.ipfs),
252+
Qs(Query { deposit_assets }),
253+
Some(TypedHeader(UserAgent::from_static(
254+
LINUX_FIREFOX_USER_AGENT,
255+
))),
256+
headers,
257+
)
258+
.await
259+
.expect("Should return response")
260+
.0;
261+
262+
// pub targeting_input_base: Input,
263+
// assert_eq!(response.targeting_input_base);
264+
assert!(response.accepted_referrers.is_empty());
265+
let expected_fallback_until = ResponseAdUnit {
266+
ipfs: fallback_unit.ipfs,
267+
media_url: fallback_unit.media_url,
268+
media_mime: fallback_unit.media_mime,
269+
target_url: fallback_unit.target_url,
270+
};
271+
assert_eq!(response.fallback_unit, Some(expected_fallback_until));
272+
273+
let expected_campaigns = vec![ResponseCampaign {
274+
campaign: campaigns[2].context.clone(),
275+
units_with_price: vec![
276+
UnitsWithPrice {
277+
unit: (&campaigns[2].context.ad_units[0]).into(),
278+
price: campaigns[2].context.pricing_bounds[&IMPRESSION].min,
279+
},
280+
UnitsWithPrice {
281+
unit: (&campaigns[2].context.ad_units[1]).into(),
282+
price: campaigns[2].context.pricing_bounds[&IMPRESSION].min,
283+
},
284+
],
285+
}];
286+
assert_eq!(response.campaigns, expected_campaigns);
287+
}
288+
289+
#[tokio::test]
290+
async fn test_non_active_campaign() {}
291+
292+
#[tokio::test]
293+
async fn test_creator_is_publisher() {}
294+
295+
#[tokio::test]
296+
async fn test_no_ad_units() {}
297+
298+
#[tokio::test]
299+
async fn test_price_less_than_min_per_impression() {}
300+
301+
#[tokio::test]
302+
async fn test_non_matching_deposit_asset() {}
303+
304+
#[tokio::test]
305+
async fn test_multiple_campaigns() {}
306+
307+
#[tokio::test]
308+
#[ignore = "exists to print output for comparison"]
309+
async fn get_sample_units_for_slot_output() {}

0 commit comments

Comments
 (0)