pub mod request;
pub mod response;
pub use request::{
Asset, AssetBuilder, Data, DataBuilder, EventTracker, EventTrackerBuilder, Image, ImageBuilder,
NativeRequest, NativeRequestBuilder, Title, TitleBuilder, Video, VideoBuilder,
};
pub use response::{
AssetResponse, AssetResponseBuilder, DataResponse, DataResponseBuilder, EventTrackerResponse,
EventTrackerResponseBuilder, ImageResponse, ImageResponseBuilder, Link, LinkBuilder,
NativeResponse, NativeResponseBuilder, TitleResponse, TitleResponseBuilder, VideoResponse,
VideoResponseBuilder,
};
#[cfg(test)]
mod integration_tests {
use super::*;
#[test]
fn test_complete_request_response_cycle() {
let request = NativeRequest::builder()
.ver("1.2")
.context(Some(1)) .plcmttype(Some(1)) .plcmtcnt(Some(1))
.seq(Some(0))
.assets(vec![
Asset::builder()
.id(1)
.required(Some(1))
.title(Some(Title::builder().len(90).build().unwrap()))
.build()
.unwrap(),
Asset::builder()
.id(2)
.img(Some(
Image::builder()
.type_(Some(3)) .w(Some(1200))
.h(Some(627))
.build()
.unwrap(),
))
.build()
.unwrap(),
Asset::builder()
.id(3)
.data(Some(
Data::builder()
.type_(2) .len(Some(140))
.build()
.unwrap(),
))
.build()
.unwrap(),
])
.eventtrackers(Some(vec![
EventTracker::builder()
.event(1) .methods(vec![1, 2]) .build()
.unwrap(),
]))
.privacy(Some(1))
.build()
.unwrap();
let request_json = serde_json::to_string(&request).unwrap();
assert!(request_json.contains("\"ver\":\"1.2\""));
assert!(request_json.contains("\"assets\""));
let response = NativeResponse::builder()
.ver("1.2")
.assets(vec![
AssetResponse::builder()
.id(1)
.required(Some(1))
.title(Some(
TitleResponse::builder()
.text("Amazing Product - Buy Now!".to_string())
.build()
.unwrap(),
))
.build()
.unwrap(),
AssetResponse::builder()
.id(2)
.img(Some(
ImageResponse::builder()
.url("https://cdn.example.com/product.jpg".to_string())
.w(Some(1200))
.h(Some(627))
.build()
.unwrap(),
))
.build()
.unwrap(),
AssetResponse::builder()
.id(3)
.data(Some(
DataResponse::builder()
.value("High-quality product with great reviews".to_string())
.build()
.unwrap(),
))
.build()
.unwrap(),
])
.link(
Link::builder()
.url("https://example.com/product".to_string())
.clicktrackers(Some(vec!["https://tracker.com/click".to_string()]))
.build()
.unwrap(),
)
.eventtrackers(Some(vec![
EventTrackerResponse::builder()
.event(1)
.method(1)
.url("https://tracker.com/imp".to_string())
.build()
.unwrap(),
]))
.privacy(Some("https://example.com/privacy".to_string()))
.build()
.unwrap();
let response_json = serde_json::to_string(&response).unwrap();
assert!(response_json.contains("\"ver\":\"1.2\""));
assert!(response_json.contains("\"link\""));
assert_eq!(request.assets.len(), response.assets.len());
for (req_asset, resp_asset) in request.assets.iter().zip(response.assets.iter()) {
assert_eq!(req_asset.id, resp_asset.id);
}
}
#[test]
fn test_multi_placement_request() {
let request = NativeRequest::builder()
.ver("1.2")
.context(Some(1))
.plcmttype(Some(1))
.plcmtcnt(Some(3)) .seq(Some(0))
.assets(vec![
Asset::builder()
.id(1)
.title(Some(Title::builder().len(90).build().unwrap()))
.build()
.unwrap(),
])
.build()
.unwrap();
assert_eq!(request.plcmtcnt, Some(3));
let json = serde_json::to_string(&request).unwrap();
let parsed: NativeRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.plcmtcnt, Some(3));
}
#[test]
fn test_dco_url_support() {
let request = NativeRequest::builder()
.ver("1.2")
.assets(vec![])
.aurlsupport(Some(1))
.durlsupport(Some(1))
.build()
.unwrap();
assert_eq!(request.aurlsupport, Some(1));
assert_eq!(request.durlsupport, Some(1));
let response = NativeResponse::builder()
.assets(vec![])
.link(
Link::builder()
.url("https://example.com".to_string())
.build()
.unwrap(),
)
.assetsurl(Some("https://cdn.example.com/assets.json".to_string()))
.dcourl(Some("https://dco.example.com/creative".to_string()))
.build()
.unwrap();
assert!(response.assetsurl.is_some());
assert!(response.dcourl.is_some());
}
#[test]
fn test_deprecated_tracking_compatibility() {
let response = NativeResponse::builder()
.assets(vec![])
.link(
Link::builder()
.url("https://example.com".to_string())
.build()
.unwrap(),
)
.imptrackers(Some(vec![
"https://imp1.com".to_string(),
"https://imp2.com".to_string(),
]))
.jstracker(Some("<script>console.log('tracked');</script>".to_string()))
.build()
.unwrap();
assert!(response.imptrackers.is_some());
assert_eq!(response.imptrackers.as_ref().unwrap().len(), 2);
assert!(response.jstracker.is_some());
let json = serde_json::to_string(&response).unwrap();
let parsed: NativeResponse = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.imptrackers, response.imptrackers);
assert_eq!(parsed.jstracker, response.jstracker);
}
#[test]
fn test_video_asset_request_response() {
let request = NativeRequest::builder()
.ver("1.2")
.assets(vec![
Asset::builder()
.id(1)
.video(Some(
Video::builder()
.mimes(vec!["video/mp4".to_string(), "video/webm".to_string()])
.minduration(Some(5))
.maxduration(Some(30))
.protocols(Some(vec![2, 3, 5, 6]))
.build()
.unwrap(),
))
.build()
.unwrap(),
])
.build()
.unwrap();
let video = request.assets[0].video.as_ref().unwrap();
assert_eq!(video.mimes.len(), 2);
assert_eq!(video.minduration, Some(5));
assert_eq!(video.maxduration, Some(30));
let response = NativeResponse::builder()
.assets(vec![
AssetResponse::builder()
.id(1)
.video(Some(
VideoResponse::builder()
.vasttag("<VAST version=\"3.0\">...</VAST>".to_string())
.build()
.unwrap(),
))
.build()
.unwrap(),
])
.link(
Link::builder()
.url("https://example.com".to_string())
.build()
.unwrap(),
)
.build()
.unwrap();
let vast = &response.assets[0].video.as_ref().unwrap().vasttag;
assert!(vast.contains("VAST"));
}
#[test]
fn test_all_data_asset_types() {
let data_types = vec![
(1, "Sponsored"),
(2, "Description"),
(3, "Rating"),
(4, "Likes"),
(5, "Downloads"),
(6, "Price"),
(7, "SalePrice"),
(8, "Phone"),
(9, "Address"),
(10, "Description2"),
(11, "DisplayUrl"),
(12, "CallToAction"),
];
for (type_id, _name) in data_types {
let asset = Asset::builder()
.id(type_id)
.data(Some(
Data::builder()
.type_(type_id)
.len(Some(100))
.build()
.unwrap(),
))
.build()
.unwrap();
assert_eq!(asset.data.as_ref().unwrap().type_, type_id);
}
}
#[test]
fn test_all_image_asset_types() {
let image_types = vec![(1, "Icon"), (2, "Logo"), (3, "Main")];
for (type_id, _name) in image_types {
let asset = Asset::builder()
.id(type_id)
.img(Some(
Image::builder()
.type_(Some(type_id))
.w(Some(300))
.h(Some(250))
.build()
.unwrap(),
))
.build()
.unwrap();
assert_eq!(asset.img.as_ref().unwrap().type_, Some(type_id));
}
}
#[test]
fn test_asset_specific_link() {
let response = NativeResponse::builder()
.assets(vec![
AssetResponse::builder()
.id(1)
.title(Some(
TitleResponse::builder()
.text("Title".to_string())
.build()
.unwrap(),
))
.link(Some(
Link::builder()
.url("https://specific-asset-link.com".to_string())
.build()
.unwrap(),
))
.build()
.unwrap(),
])
.link(
Link::builder()
.url("https://default-link.com".to_string())
.build()
.unwrap(),
)
.build()
.unwrap();
let asset_link = response.assets[0].link.as_ref().unwrap();
assert_eq!(asset_link.url, "https://specific-asset-link.com");
assert_eq!(response.link.url, "https://default-link.com");
}
#[test]
fn test_roundtrip_with_extensions() {
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Default)]
struct CustomExt {
custom_field: String,
}
let request = NativeRequestBuilder::default()
.ver("1.2")
.assets(vec![])
.ext(Some(Box::new(CustomExt {
custom_field: "custom_value".to_string(),
})))
.build()
.unwrap();
let json = serde_json::to_string(&request).unwrap();
let parsed: NativeRequest<CustomExt> = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.ext.as_ref().unwrap().custom_field, "custom_value");
}
}