|
| 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