From f063d03be31d53c0c0fb00872170801ed8ea2e79 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Fri, 18 Dec 2020 11:32:28 +0200 Subject: [PATCH] /units-for-slot - update tests & fix issues --- src/market.rs | 7 +- src/units_for_slot.rs | 31 ++--- src/units_for_slot_test.rs | 233 ++++++++++++++++--------------------- 3 files changed, 123 insertions(+), 148 deletions(-) diff --git a/src/market.rs b/src/market.rs index 583786b..4248d77 100644 --- a/src/market.rs +++ b/src/market.rs @@ -1,4 +1,8 @@ -use primitives::{AdSlot, AdUnit, market::{AdSlotResponse, AdUnitResponse, AdUnitsResponse, Campaign, StatusType}, util::ApiUrl}; +use primitives::{ + market::{AdSlotResponse, AdUnitResponse, AdUnitsResponse, Campaign, StatusType}, + util::ApiUrl, + AdSlot, AdUnit, +}; use reqwest::{Client, Error, StatusCode}; use slog::{info, Logger}; use std::fmt; @@ -191,4 +195,3 @@ impl MarketApi { Ok(campaigns) } } - diff --git a/src/units_for_slot.rs b/src/units_for_slot.rs index 9622ea8..41bdb6d 100644 --- a/src/units_for_slot.rs +++ b/src/units_for_slot.rs @@ -1,20 +1,19 @@ use crate::{ cache::{Cache, Campaign, Client}, - market::AdSlotResponse, not_found, service_unavailable, status::Status, Config, Error, MarketApi, ROUTE_UNITS_FOR_SLOT, }; use chrono::Utc; -use http::header::HeaderName; +use http::header::{HeaderName, CONTENT_TYPE}; use hyper::{header::USER_AGENT, Body, Request, Response}; use input::Input; use primitives::{ + market::AdSlotResponse, supermarket::units_for_slot::response, - supermarket::units_for_slot::response::{AdUnit, Response as UnitsForSlotResponse}, - targeting::eval_with_callback, - targeting::{get_pricing_bounds, input, Output}, - ValidatorId, + supermarket::units_for_slot::response::Response as UnitsForSlotResponse, + targeting::{eval_with_callback, get_pricing_bounds, input, Output}, + AdUnit, ValidatorId, }; use slog::{error, info, warn, Logger}; use std::sync::Arc; @@ -71,7 +70,7 @@ pub async fn get_units_for_slot( }; let accepted_referrers = ad_slot_response.accepted_referrers.clone(); - let units_ipfses: Vec = units.iter().map(|au| au.id.to_string()).collect(); + let units_ipfses: Vec = units.iter().map(|au| au.ipfs.to_string()).collect(); let fallback_unit: Option = match ad_slot_response.slot.fallback_unit.as_ref() { Some(unit_ipfs) => { let ad_unit_response = match market.fetch_unit(&unit_ipfs).await { @@ -170,7 +169,7 @@ pub async fn get_units_for_slot( let campaigns_limited_by_earner = get_campaigns(cache, config, &deposit_assets, publisher_id).await; - info!(&logger, "Fetched Cache campaigns"; "length" => campaigns_limited_by_earner.len(), "publisher_id" => %publisher_id); + info!(&logger, "Fetched Cache campaigns limited by earner (publisher)"; "length" => campaigns_limited_by_earner.len(), "publisher_id" => %publisher_id); // We return those in the result (which means AdView would have those) but we don't actually use them // we do that in order to have the same variables as the validator, so that the `price` is the same @@ -186,11 +185,10 @@ pub async fn get_units_for_slot( ad_slot_id: ad_slot_response.slot.ipfs.clone(), ad_slot_type: ad_slot_response.slot.ad_type.clone(), publisher_id, - country: country.clone(), + country, event_type: "IMPRESSION".to_string(), - // TODO: Replace with [unsigned_abs](https://doc.rust-lang.org/std/primitive.i64.html#method.unsigned_abs) once it's stabilized seconds_since_epoch: Utc::now(), - user_agent_os: user_agent_os.clone(), + user_agent_os, user_agent_browser_family: user_agent_browser_family.clone(), }, ad_unit_id: None, @@ -214,10 +212,14 @@ pub async fn get_units_for_slot( targeting_input_base, accepted_referrers, campaigns, - fallback_unit, + fallback_unit: fallback_unit.map(|ad_unit| response::AdUnit::from(&ad_unit)), }; - Ok(Response::new(Body::from(serde_json::to_string(&response)?))) + Ok(Response::builder() + .status(http::StatusCode::OK) + .header(CONTENT_TYPE, "application/json") + .body(Body::from(serde_json::to_string(&response)?)) + .expect("Should create response")) } } @@ -236,7 +238,8 @@ async fn get_campaigns( // The Supermarket has the Active status combining Active & Ready from Market if campaign.status == Status::Active && campaign.channel.creator != publisher_id - && deposit_assets.contains(&campaign.channel.deposit_asset) + && (deposit_assets.is_empty() + || deposit_assets.contains(&campaign.channel.deposit_asset)) { Some(campaign) } else { diff --git a/src/units_for_slot_test.rs b/src/units_for_slot_test.rs index 458d8d9..d4797cc 100644 --- a/src/units_for_slot_test.rs +++ b/src/units_for_slot_test.rs @@ -5,16 +5,13 @@ use http::request::Request; use hyper::Body; use primitives::{ supermarket::units_for_slot::response::{ - AdUnit, Campaign as ResponseCampaign, Channel as ResponseChannel, UnitsWithPrice, + Campaign as ResponseCampaign, Channel as ResponseChannel, UnitsWithPrice, }, targeting::{input, Function, Rule, Value}, - util::tests::prep_db::{DUMMY_CHANNEL, IDS}, - AdSlot, BigNum, IPFS, + util::tests::prep_db::{DUMMY_AD_UNITS, DUMMY_CHANNEL, IDS}, + AdSlot, BigNum, }; -use std::iter::Iterator; -use std::str::FromStr; -use std::sync::Arc; -use std::{collections::HashMap, convert::TryFrom}; +use std::{collections::HashMap, iter::Iterator, str::FromStr, sync::Arc}; use url::Url; use wiremock::{ matchers::{method, path}, @@ -22,10 +19,11 @@ use wiremock::{ }; mod units_for_slot_tests { + use super::*; use chrono::DateTime; use http::header::USER_AGENT; - use primitives::{Channel, ChannelId, targeting::Rules}; + use primitives::{market::AdUnitsResponse, targeting::Rules, Channel, ChannelId}; // User Agent OS: Linux (only in `woothee`) // User Agent Browser Family: Firefox @@ -34,8 +32,9 @@ mod units_for_slot_tests { // uses two-letter country codes: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 const TEST_CLOUDFLARE_IPCOUNTY: &str = "BG"; - fn get_mock_campaign(channel: Channel, units: &[AdUnit]) -> ResponseCampaign { - let units_with_price = get_units_with_price(&channel, &units); + /// Uses the Channel AdUnits as UnitsWithPrice for the response + fn get_mock_campaign(channel: Channel) -> ResponseCampaign { + let units_with_price = get_units_with_price(&channel); let targeting_rules = channel.spec.targeting_rules.clone(); ResponseCampaign { channel: ResponseChannel::from(channel), @@ -44,50 +43,18 @@ mod units_for_slot_tests { } } - fn get_units_with_price(channel: &Channel, units: &[AdUnit]) -> Vec { - units + fn get_units_with_price(channel: &Channel) -> Vec { + channel + .spec + .ad_units .iter() - .cloned() .map(|u| UnitsWithPrice { - unit: u, + unit: u.into(), price: channel.spec.min_per_impression.clone(), }) .collect() } - fn get_supermarket_ad_units() -> Vec { - vec![ - AdUnit { - id: IPFS::try_from("Qmasg8FrbuSQpjFu3kRnZF9beg8rEBFrqgi1uXDRwCbX5f") - .expect("should convert"), - media_url: "ipfs://QmcUVX7fvoLMM93uN2bD3wGTH8MXSxeL8hojYfL2Lhp7mR".to_string(), - media_mime: "image/jpeg".to_string(), - target_url: "https://www.adex.network/?stremio-test-banner-1".to_string(), - }, - AdUnit { - id: IPFS::try_from("QmVhRDGXoM3Fg3HZD5xwMuxtb9ZErwC8wHt8CjsfxaiUbZ") - .expect("should convert"), - media_url: "ipfs://QmQB7uz7Gxfy7wqAnrnBcZFaVJLos8J9gn8mRcHQU6dAi1".to_string(), - media_mime: "image/jpeg".to_string(), - target_url: "https://www.adex.network/?adex-campaign=true&pub=stremio".to_string(), - }, - AdUnit { - id: IPFS::try_from("QmYwcpMjmqJfo9ot1jGe9rfXsszFV1WbEA59QS7dEVHfJi") - .expect("should convert"), - media_url: "ipfs://QmQB7uz7Gxfy7wqAnrnBcZFaVJLos8J9gn8mRcHQU6dAi1".to_string(), - media_mime: "image/jpeg".to_string(), - target_url: "https://www.adex.network/?adex-campaign=true".to_string(), - }, - AdUnit { - id: IPFS::try_from("QmTAF3FsFDS7Ru8WChoD9ofiHTH8gAQfR4mYSnwxqTDpJH") - .expect("should convert"), - media_url: "ipfs://QmQAcfBJpDDuH99A4p3pFtUmQwamS8UYStP5HxHC7bgYXY".to_string(), - media_mime: "image/jpeg".to_string(), - target_url: "https://adex.network".to_string(), - }, - ] - } - fn get_mock_rules(categories: &[&str]) -> Vec { let get_rule = Function::new_get("adSlot.categories"); let categories_array = @@ -166,80 +133,16 @@ mod units_for_slot_tests { } } - fn mock_channel_units() -> Vec { - vec![ - primitives::AdUnit { - ipfs: IPFS::try_from("Qmasg8FrbuSQpjFu3kRnZF9beg8rEBFrqgi1uXDRwCbX5f") - .expect("should convert"), - media_url: "ipfs://QmcUVX7fvoLMM93uN2bD3wGTH8MXSxeL8hojYfL2Lhp7mR".to_string(), - media_mime: "image/jpeg".to_string(), - target_url: "https://www.adex.network/?stremio-test-banner-1".to_string(), - archived: false, - description: Some("test description".to_string()), - ad_type: "legacy_250x250".to_string(), - created: Utc.timestamp(1_564_383_600, 0), - min_targeting_score: Some(1.00), - modified: None, - owner: IDS["publisher"], - title: Some("test title".to_string()), - }, - primitives::AdUnit { - ipfs: IPFS::try_from("QmVhRDGXoM3Fg3HZD5xwMuxtb9ZErwC8wHt8CjsfxaiUbZ") - .expect("should convert"), - media_url: "ipfs://QmQB7uz7Gxfy7wqAnrnBcZFaVJLos8J9gn8mRcHQU6dAi1".to_string(), - media_mime: "image/jpeg".to_string(), - target_url: "https://www.adex.network/?adex-campaign=true&pub=stremio".to_string(), - archived: false, - description: Some("test description".to_string()), - ad_type: "legacy_250x250".to_string(), - created: Utc.timestamp(1_564_383_600, 0), - min_targeting_score: Some(1.00), - modified: None, - owner: IDS["publisher"], - title: Some("test title".to_string()), - }, - primitives::AdUnit { - ipfs: IPFS::try_from("QmYwcpMjmqJfo9ot1jGe9rfXsszFV1WbEA59QS7dEVHfJi") - .expect("should convert"), - media_url: "ipfs://QmQB7uz7Gxfy7wqAnrnBcZFaVJLos8J9gn8mRcHQU6dAi1".to_string(), - media_mime: "image/jpeg".to_string(), - target_url: "https://www.adex.network/?adex-campaign=true".to_string(), - archived: false, - description: Some("test description".to_string()), - ad_type: "legacy_250x250".to_string(), - created: Utc.timestamp(1_564_383_600, 0), - min_targeting_score: Some(1.00), - modified: None, - owner: IDS["publisher"], - title: Some("test title".to_string()), - }, - primitives::AdUnit { - ipfs: IPFS::try_from("QmTAF3FsFDS7Ru8WChoD9ofiHTH8gAQfR4mYSnwxqTDpJH") - .expect("should convert"), - media_url: "ipfs://QmQAcfBJpDDuH99A4p3pFtUmQwamS8UYStP5HxHC7bgYXY".to_string(), - media_mime: "image/jpeg".to_string(), - target_url: "https://adex.network".to_string(), - archived: false, - description: Some("test description".to_string()), - ad_type: "legacy_250x250".to_string(), - created: Utc.timestamp(1_564_383_600, 0), - min_targeting_score: Some(1.00), - modified: None, - owner: IDS["publisher"], - title: Some("test title".to_string()), - }, - ] - } - fn mock_channel(rules: &[Rule]) -> Channel { let mut channel = DUMMY_CHANNEL.clone(); - channel.spec.ad_units = mock_channel_units(); + channel.spec.ad_units = DUMMY_AD_UNITS.to_vec(); // NOTE: always set the spec.targeting_rules first channel.spec.targeting_rules = Rules(rules.to_vec()); channel.spec.min_per_impression = 100_000_000_000_000.into(); channel.spec.max_per_impression = 1_000_000_000_000_000.into(); - channel.spec.active_from = Some(Utc.timestamp_millis(1_606_136_400_000)); + // Timestamp: 1_606_136_400_000 + channel.spec.active_from = Some(Utc.ymd(2020, 11, 23).and_hms(15, 0, 0)); channel } @@ -311,12 +214,20 @@ mod units_for_slot_tests { let mock_cache = Cache::initialize(mock_client).await; - let ad_units = get_supermarket_ad_units(); + let market_ad_units = AdUnitsResponse( + channel + .spec + .ad_units + .clone() + .into_iter() + .map(Into::into) + .collect(), + ); let mock_slot = get_supermarket_ad_slot(&rules, &categories); Mock::given(method("GET")) .and(path("/market/units")) - .respond_with(ResponseTemplate::new(200).set_body_json(&ad_units)) + .respond_with(ResponseTemplate::new(200).set_body_json(&market_ad_units)) .mount(&server) .await; @@ -325,7 +236,8 @@ mod units_for_slot_tests { .respond_with(ResponseTemplate::new(200).set_body_json(&mock_slot)) .mount(&server) .await; - let campaign = get_mock_campaign(channel.clone(), &ad_units); + + let campaign = get_mock_campaign(channel.clone()); let request = Request::get(format!( "/units-for-slot/{}?depositAsset={}", @@ -403,12 +315,20 @@ mod units_for_slot_tests { let mock_cache = Cache::initialize(mock_client).await; - let ad_units = get_supermarket_ad_units(); + let market_ad_units = AdUnitsResponse( + channel + .spec + .ad_units + .clone() + .into_iter() + .map(Into::into) + .collect(), + ); let mock_slot = get_supermarket_ad_slot(&rules, &categories); Mock::given(method("GET")) .and(path("/market/units")) - .respond_with(ResponseTemplate::new(200).set_body_json(&ad_units)) + .respond_with(ResponseTemplate::new(200).set_body_json(&market_ad_units)) .mount(&server) .await; @@ -483,6 +403,7 @@ mod units_for_slot_tests { let rules = get_mock_rules(&categories); let mut channel = mock_channel(&rules); channel.creator = IDS["publisher"]; + let config = crate::config::DEVELOPMENT.clone(); let mock_client = MockClient::init( vec![mock_cache_campaign(channel.clone(), Status::Active)], @@ -493,12 +414,20 @@ mod units_for_slot_tests { let mock_cache = Cache::initialize(mock_client).await; - let ad_units = get_supermarket_ad_units(); + let market_ad_units = AdUnitsResponse( + channel + .spec + .ad_units + .clone() + .into_iter() + .map(Into::into) + .collect(), + ); let mock_slot = get_supermarket_ad_slot(&rules, &categories); Mock::given(method("GET")) .and(path("/market/units")) - .respond_with(ResponseTemplate::new(200).set_body_json(&ad_units)) + .respond_with(ResponseTemplate::new(200).set_body_json(&market_ad_units)) .mount(&server) .await; @@ -585,12 +514,20 @@ mod units_for_slot_tests { let mock_cache = Cache::initialize(mock_client).await; - let ad_units: Vec = get_supermarket_ad_units(); + let market_ad_units = AdUnitsResponse( + channel + .spec + .ad_units + .clone() + .into_iter() + .map(Into::into) + .collect(), + ); let mock_slot = get_supermarket_ad_slot(&rules, &categories); Mock::given(method("GET")) .and(path("/market/units")) - .respond_with(ResponseTemplate::new(200).set_body_json(&ad_units)) + .respond_with(ResponseTemplate::new(200).set_body_json(&market_ad_units)) .mount(&server) .await; @@ -675,12 +612,20 @@ mod units_for_slot_tests { let mock_cache = Cache::initialize(mock_client).await; - let ad_units = get_supermarket_ad_units(); + let market_ad_units = AdUnitsResponse( + channel + .spec + .ad_units + .clone() + .into_iter() + .map(Into::into) + .collect(), + ); let mock_slot = get_supermarket_ad_slot(&rules, &categories); Mock::given(method("GET")) .and(path("/market/units")) - .respond_with(ResponseTemplate::new(200).set_body_json(&ad_units)) + .respond_with(ResponseTemplate::new(200).set_body_json(&market_ad_units)) .mount(&server) .await; @@ -765,12 +710,20 @@ mod units_for_slot_tests { let mock_cache = Cache::initialize(mock_client).await; - let ad_units = get_supermarket_ad_units(); + let market_ad_units = AdUnitsResponse( + channel + .spec + .ad_units + .clone() + .into_iter() + .map(Into::into) + .collect(), + ); let mock_slot = get_supermarket_ad_slot(&rules, &categories); Mock::given(method("GET")) .and(path("/market/units")) - .respond_with(ResponseTemplate::new(200).set_body_json(&ad_units)) + .respond_with(ResponseTemplate::new(200).set_body_json(&market_ad_units)) .mount(&server) .await; @@ -862,12 +815,20 @@ mod units_for_slot_tests { let mock_cache = Cache::initialize(mock_client).await; - let ad_units = get_supermarket_ad_units(); + let market_ad_units = AdUnitsResponse( + channel + .spec + .ad_units + .clone() + .into_iter() + .map(Into::into) + .collect(), + ); let mock_slot = get_supermarket_ad_slot(&rules, &categories); Mock::given(method("GET")) .and(path("/market/units")) - .respond_with(ResponseTemplate::new(200).set_body_json(&ad_units)) + .respond_with(ResponseTemplate::new(200).set_body_json(&market_ad_units)) .mount(&server) .await; @@ -876,7 +837,7 @@ mod units_for_slot_tests { .respond_with(ResponseTemplate::new(200).set_body_json(&mock_slot)) .mount(&server) .await; - let campaign = get_mock_campaign(channel.clone(), &ad_units); + let campaign = get_mock_campaign(channel.clone()); let request = Request::get(format!( "/units-for-slot/{}?depositAsset={}", @@ -955,12 +916,20 @@ mod units_for_slot_tests { let mock_cache = Cache::initialize(mock_client).await; - let ad_units = get_supermarket_ad_units(); + let market_ad_units = AdUnitsResponse( + channel + .spec + .ad_units + .clone() + .into_iter() + .map(Into::into) + .collect(), + ); let mock_slot = get_supermarket_ad_slot(&rules, &categories); Mock::given(method("GET")) .and(path("/market/units")) - .respond_with(ResponseTemplate::new(200).set_body_json(&ad_units)) + .respond_with(ResponseTemplate::new(200).set_body_json(&market_ad_units)) .mount(&server) .await;