use crate::Extension;
use derive_builder::Builder;
use serde::{Deserialize, Serialize};
#[derive(Builder, Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
#[builder(build_fn(error = "crate::Error"), default)]
#[serde(bound(serialize = "Ext: Extension", deserialize = "Ext: Extension"))]
pub struct NativeRequest<Ext: Extension = crate::DefaultExt> {
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default, setter(into, strip_option))]
pub ver: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub context: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub contextsubtype: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub plcmttype: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub plcmtcnt: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub seq: Option<i32>,
#[builder(default, setter(into))]
pub assets: Vec<Asset<Ext>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub aurlsupport: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub durlsupport: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub eventtrackers: Option<Vec<EventTracker<Ext>>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub privacy: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub ext: Option<Box<Ext>>,
}
impl NativeRequest {
pub fn builder() -> NativeRequestBuilder {
NativeRequestBuilder::create_empty()
}
}
#[derive(Builder, Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
#[builder(build_fn(error = "crate::Error"), default)]
#[serde(bound(serialize = "Ext: Extension", deserialize = "Ext: Extension"))]
pub struct Asset<Ext: Extension = crate::DefaultExt> {
#[builder(setter(into))]
pub id: i32,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub required: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub title: Option<Title<Ext>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub img: Option<Image<Ext>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub video: Option<Video<Ext>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub data: Option<Data<Ext>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub ext: Option<Box<Ext>>,
}
impl Asset {
pub fn builder() -> AssetBuilder {
AssetBuilder::create_empty()
}
}
#[derive(Builder, Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
#[builder(build_fn(error = "crate::Error"), default)]
#[serde(bound(serialize = "Ext: Extension", deserialize = "Ext: Extension"))]
pub struct Title<Ext: Extension = crate::DefaultExt> {
#[builder(setter(into))]
pub len: i32,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub ext: Option<Box<Ext>>,
}
impl Title {
pub fn builder() -> TitleBuilder {
TitleBuilder::create_empty()
}
}
#[derive(Builder, Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
#[builder(build_fn(error = "crate::Error"), default)]
#[serde(bound(serialize = "Ext: Extension", deserialize = "Ext: Extension"))]
pub struct Image<Ext: Extension = crate::DefaultExt> {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "type")]
#[builder(default)]
pub type_: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub w: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub h: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub wmin: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub hmin: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub wmax: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub hmax: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub mimes: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub ext: Option<Box<Ext>>,
}
impl Image {
pub fn builder() -> ImageBuilder {
ImageBuilder::create_empty()
}
}
#[derive(Builder, Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
#[builder(build_fn(error = "crate::Error"), default)]
#[serde(bound(serialize = "Ext: Extension", deserialize = "Ext: Extension"))]
pub struct Video<Ext: Extension = crate::DefaultExt> {
#[builder(default, setter(into))]
pub mimes: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub minduration: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub maxduration: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub protocols: Option<Vec<i32>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub ext: Option<Box<Ext>>,
}
impl Video {
pub fn builder() -> VideoBuilder {
VideoBuilder::create_empty()
}
}
#[derive(Builder, Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
#[builder(build_fn(error = "crate::Error"), default)]
#[serde(bound(serialize = "Ext: Extension", deserialize = "Ext: Extension"))]
pub struct Data<Ext: Extension = crate::DefaultExt> {
#[serde(rename = "type")]
#[builder(setter(into))]
pub type_: i32,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub len: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub ext: Option<Box<Ext>>,
}
impl Data {
pub fn builder() -> DataBuilder {
DataBuilder::create_empty()
}
}
#[derive(Builder, Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
#[builder(build_fn(error = "crate::Error"), default)]
#[serde(bound(serialize = "Ext: Extension", deserialize = "Ext: Extension"))]
pub struct EventTracker<Ext: Extension = crate::DefaultExt> {
#[builder(setter(into))]
pub event: i32,
#[builder(default, setter(into))]
pub methods: Vec<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub ext: Option<Box<Ext>>,
}
impl EventTracker {
pub fn builder() -> EventTrackerBuilder {
EventTrackerBuilder::create_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_native_request_creation() {
let request = NativeRequest::builder()
.ver("1.2")
.context(Some(1))
.plcmttype(Some(1))
.assets(vec![])
.build()
.unwrap();
assert_eq!(request.ver, Some("1.2".to_string()));
assert_eq!(request.context, Some(1));
assert_eq!(request.plcmttype, Some(1));
}
#[test]
fn test_native_request_serialization() {
let request = NativeRequest::builder()
.ver("1.2")
.assets(vec![])
.build()
.unwrap();
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains(r#""ver":"1.2""#));
}
#[test]
fn test_native_request_deserialization() {
let json = r#"{"ver":"1.2","assets":[]}"#;
let request: NativeRequest = serde_json::from_str(json).unwrap();
assert_eq!(request.ver, Some("1.2".to_string()));
assert_eq!(request.assets.len(), 0);
}
#[test]
fn test_asset_with_title() {
let asset = Asset::builder()
.id(1)
.required(Some(1))
.title(Some(Title::builder().len(90).build().unwrap()))
.build()
.unwrap();
assert_eq!(asset.id, 1);
assert!(asset.title.is_some());
assert_eq!(asset.title.as_ref().unwrap().len, 90);
}
#[test]
fn test_asset_with_image() {
let asset = Asset::builder()
.id(2)
.img(Some(
Image::builder()
.type_(Some(3))
.w(Some(1200))
.h(Some(627))
.build()
.unwrap(),
))
.build()
.unwrap();
assert_eq!(asset.id, 2);
assert!(asset.img.is_some());
let img = asset.img.as_ref().unwrap();
assert_eq!(img.type_, Some(3));
assert_eq!(img.w, Some(1200));
assert_eq!(img.h, Some(627));
}
#[test]
fn test_asset_serialization() {
let asset = Asset::builder()
.id(1)
.title(Some(Title::builder().len(25).build().unwrap()))
.build()
.unwrap();
let json = serde_json::to_string(&asset).unwrap();
assert!(json.contains(r#""id":1"#));
assert!(json.contains(r#""len":25"#));
}
#[test]
fn test_title_creation() {
let title = Title::builder().len(90).build().unwrap();
assert_eq!(title.len, 90);
}
#[test]
fn test_image_with_dimensions() {
let image = Image::builder()
.type_(Some(1))
.w(Some(300))
.h(Some(250))
.wmin(Some(200))
.hmin(Some(150))
.build()
.unwrap();
assert_eq!(image.type_, Some(1));
assert_eq!(image.w, Some(300));
assert_eq!(image.h, Some(250));
assert_eq!(image.wmin, Some(200));
assert_eq!(image.hmin, Some(150));
}
#[test]
fn test_image_with_mimes() {
let image = Image::builder()
.mimes(Some(vec![
"image/jpeg".to_string(),
"image/png".to_string(),
]))
.build()
.unwrap();
assert!(image.mimes.is_some());
let mimes = image.mimes.as_ref().unwrap();
assert_eq!(mimes.len(), 2);
assert!(mimes.contains(&"image/jpeg".to_string()));
}
#[test]
fn test_video_creation() {
let video = Video::builder()
.mimes(vec!["video/mp4".to_string()])
.minduration(Some(5))
.maxduration(Some(30))
.build()
.unwrap();
assert_eq!(video.mimes.len(), 1);
assert_eq!(video.minduration, Some(5));
assert_eq!(video.maxduration, Some(30));
}
#[test]
fn test_data_creation() {
let data = Data::builder().type_(2).len(Some(140)).build().unwrap();
assert_eq!(data.type_, 2);
assert_eq!(data.len, Some(140));
}
#[test]
fn test_event_tracker_creation() {
let tracker = EventTracker::builder()
.event(1)
.methods(vec![1, 2])
.build()
.unwrap();
assert_eq!(tracker.event, 1);
assert_eq!(tracker.methods.len(), 2);
assert_eq!(tracker.methods[0], 1);
assert_eq!(tracker.methods[1], 2);
}
#[test]
fn test_complete_native_request() {
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();
assert_eq!(request.assets.len(), 3);
assert!(request.eventtrackers.is_some());
assert_eq!(request.privacy, Some(1));
}
#[test]
fn test_native_request_roundtrip() {
let original = NativeRequest::builder()
.ver("1.2")
.context(Some(1))
.plcmttype(Some(1))
.assets(vec![
Asset::builder()
.id(1)
.title(Some(Title::builder().len(90).build().unwrap()))
.build()
.unwrap(),
])
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
let parsed: NativeRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.ver, original.ver);
assert_eq!(parsed.context, original.context);
assert_eq!(parsed.assets.len(), original.assets.len());
}
#[test]
fn test_optional_fields_omitted_in_json() {
let request = NativeRequest::builder().assets(vec![]).build().unwrap();
let json = serde_json::to_string(&request).unwrap();
assert!(!json.contains("\"ver\""));
assert!(!json.contains("\"context\""));
assert!(!json.contains("\"plcmttype\""));
assert!(!json.contains("\"privacy\""));
}
#[test]
fn test_data_type_field_serialization() {
let data = Data::builder().type_(2).build().unwrap();
let json = serde_json::to_string(&data).unwrap();
assert!(json.contains(r#""type":2"#));
assert!(!json.contains("type_"));
}
#[test]
fn test_image_type_field_serialization() {
let image = Image::builder().type_(Some(3)).build().unwrap();
let json = serde_json::to_string(&image).unwrap();
assert!(json.contains(r#""type":3"#));
assert!(!json.contains("type_"));
}
#[test]
fn test_empty_assets_array() {
let request = NativeRequest::builder()
.ver("1.2")
.assets(vec![])
.build()
.unwrap();
assert_eq!(request.assets.len(), 0);
let json = serde_json::to_string(&request).unwrap();
let parsed: NativeRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.assets.len(), 0);
}
#[test]
fn test_asset_with_no_asset_type() {
let asset = Asset::builder().id(1).build().unwrap();
assert_eq!(asset.id, 1);
assert!(asset.title.is_none());
assert!(asset.img.is_none());
assert!(asset.video.is_none());
assert!(asset.data.is_none());
}
#[test]
fn test_asset_with_multiple_types() {
let asset = Asset::builder()
.id(1)
.title(Some(Title::builder().len(25).build().unwrap()))
.img(Some(Image::builder().type_(Some(3)).build().unwrap()))
.build()
.unwrap();
assert!(asset.title.is_some());
assert!(asset.img.is_some());
}
#[test]
fn test_minimal_native_request() {
let request = NativeRequest::builder().assets(vec![]).build().unwrap();
assert!(request.ver.is_none());
assert!(request.context.is_none());
assert_eq!(request.assets.len(), 0);
}
#[test]
fn test_minimal_title() {
let title = Title::builder().len(90).build().unwrap();
assert_eq!(title.len, 90);
assert!(title.ext.is_none());
}
#[test]
fn test_minimal_image() {
let image = Image::builder().build().unwrap();
assert!(image.type_.is_none());
assert!(image.w.is_none());
assert!(image.h.is_none());
assert!(image.mimes.is_none());
}
#[test]
fn test_image_with_max_dimensions() {
let image = Image::builder()
.wmin(Some(600))
.hmin(Some(314))
.wmax(Some(1920))
.hmax(Some(1080))
.build()
.unwrap();
assert_eq!(image.wmin, Some(600));
assert_eq!(image.hmin, Some(314));
assert_eq!(image.wmax, Some(1920));
assert_eq!(image.hmax, Some(1080));
}
#[test]
fn test_minimal_video() {
let video = Video::builder()
.mimes(vec!["video/mp4".to_string()])
.build()
.unwrap();
assert_eq!(video.mimes.len(), 1);
assert!(video.minduration.is_none());
assert!(video.maxduration.is_none());
}
#[test]
fn test_minimal_data() {
let data = Data::builder().type_(2).build().unwrap();
assert_eq!(data.type_, 2);
assert!(data.len.is_none());
}
#[test]
fn test_minimal_event_tracker() {
let tracker = EventTracker::builder()
.event(1)
.methods(vec![1])
.build()
.unwrap();
assert_eq!(tracker.event, 1);
assert_eq!(tracker.methods.len(), 1);
}
#[test]
fn test_deserialization_with_unknown_fields() {
let json = r#"{
"ver": "1.2",
"assets": [],
"unknown_field": "should be ignored",
"another_unknown": 123
}"#;
let result: Result<NativeRequest, _> = serde_json::from_str(json);
assert!(result.is_ok());
let request = result.unwrap();
assert_eq!(request.ver, Some("1.2".to_string()));
}
#[test]
fn test_deserialization_with_null_optional_fields() {
let json = r#"{
"ver": null,
"context": null,
"assets": []
}"#;
let result: Result<NativeRequest, _> = serde_json::from_str(json);
assert!(result.is_ok());
let request = result.unwrap();
assert!(request.ver.is_none());
assert!(request.context.is_none());
}
#[test]
fn test_all_context_types() {
for context_type in 1..=3 {
let request = NativeRequest::builder()
.context(Some(context_type))
.assets(vec![])
.build()
.unwrap();
assert_eq!(request.context, Some(context_type));
}
}
#[test]
fn test_all_placement_types() {
for placement_type in 1..=4 {
let request = NativeRequest::builder()
.plcmttype(Some(placement_type))
.assets(vec![])
.build()
.unwrap();
assert_eq!(request.plcmttype, Some(placement_type));
}
}
#[test]
fn test_multi_placement_request() {
let request = NativeRequest::builder()
.plcmtcnt(Some(5))
.seq(Some(2))
.assets(vec![])
.build()
.unwrap();
assert_eq!(request.plcmtcnt, Some(5));
assert_eq!(request.seq, Some(2));
}
#[test]
fn test_dco_flags() {
let request = NativeRequest::builder()
.aurlsupport(Some(1))
.durlsupport(Some(1))
.assets(vec![])
.build()
.unwrap();
assert_eq!(request.aurlsupport, Some(1));
assert_eq!(request.durlsupport, Some(1));
}
#[test]
fn test_context_with_invalid_value() {
let json = r#"{"context":99,"assets":[]}"#;
let result: Result<NativeRequest, _> = serde_json::from_str(json);
assert!(result.is_ok(), "Invalid context value 99 currently passes");
assert_eq!(result.unwrap().context, Some(99));
}
#[test]
fn test_context_with_zero() {
let json = r#"{"context":0,"assets":[]}"#;
let result: Result<NativeRequest, _> = serde_json::from_str(json);
assert!(result.is_ok(), "Zero context value currently passes");
assert_eq!(result.unwrap().context, Some(0));
}
#[test]
fn test_contextsubtype_with_invalid_value() {
let json = r#"{"contextsubtype":999,"assets":[]}"#;
let result: Result<NativeRequest, _> = serde_json::from_str(json);
assert!(result.is_ok(), "Invalid contextsubtype currently passes");
assert_eq!(result.unwrap().contextsubtype, Some(999));
}
#[test]
fn test_plcmttype_with_invalid_value() {
let json = r#"{"plcmttype":99,"assets":[]}"#;
let result: Result<NativeRequest, _> = serde_json::from_str(json);
assert!(
result.is_ok(),
"Invalid plcmttype value 99 currently passes"
);
assert_eq!(result.unwrap().plcmttype, Some(99));
}
#[test]
fn test_plcmttype_with_zero() {
let json = r#"{"plcmttype":0,"assets":[]}"#;
let result: Result<NativeRequest, _> = serde_json::from_str(json);
assert!(result.is_ok(), "Zero plcmttype value currently passes");
assert_eq!(result.unwrap().plcmttype, Some(0));
}
#[test]
fn test_negative_enum_values() {
let json = r#"{"context":-1,"plcmttype":-1,"assets":[]}"#;
let result: Result<NativeRequest, _> = serde_json::from_str(json);
assert!(result.is_ok(), "Negative enum values currently pass");
let request = result.unwrap();
assert_eq!(request.context, Some(-1));
assert_eq!(request.plcmttype, Some(-1));
}
#[test]
fn test_valid_context_plcmttype_combinations() {
for context in 1..=3 {
for plcmttype in 1..=4 {
let request = NativeRequest::builder()
.context(Some(context))
.plcmttype(Some(plcmttype))
.assets(vec![])
.build()
.unwrap();
assert_eq!(request.context, Some(context));
assert_eq!(request.plcmttype, Some(plcmttype));
let json = serde_json::to_string(&request).unwrap();
let deserialized: NativeRequest = serde_json::from_str(&json).unwrap();
assert_eq!(request.context, deserialized.context);
assert_eq!(request.plcmttype, deserialized.plcmttype);
}
}
}
#[test]
fn test_asset_with_multiple_type_fields_all_four() {
let title = Title::builder().len(90).build().unwrap();
let img = Image::builder().build().unwrap();
let video = Video::builder()
.mimes(vec!["video/mp4".to_string()])
.build()
.unwrap();
let data = Data::builder().type_(1).build().unwrap();
let asset = Asset::builder()
.id(1)
.title(Some(title))
.img(Some(img))
.video(Some(video))
.data(Some(data))
.build();
assert!(
asset.is_ok(),
"Asset with all four type fields currently passes"
);
let asset = asset.unwrap();
assert!(asset.title.is_some());
assert!(asset.img.is_some());
assert!(asset.video.is_some());
assert!(asset.data.is_some());
}
#[test]
fn test_asset_with_title_only() {
let title = Title::builder().len(90).build().unwrap();
let asset = Asset::builder().id(1).title(Some(title)).build().unwrap();
assert!(asset.title.is_some());
assert!(asset.img.is_none());
assert!(asset.video.is_none());
assert!(asset.data.is_none());
}
#[test]
fn test_asset_with_img_only() {
let img = Image::builder().build().unwrap();
let asset = Asset::builder().id(1).img(Some(img)).build().unwrap();
assert!(asset.title.is_none());
assert!(asset.img.is_some());
assert!(asset.video.is_none());
assert!(asset.data.is_none());
}
#[test]
fn test_asset_with_video_only() {
let video = Video::builder()
.mimes(vec!["video/mp4".to_string()])
.build()
.unwrap();
let asset = Asset::builder().id(1).video(Some(video)).build().unwrap();
assert!(asset.title.is_none());
assert!(asset.img.is_none());
assert!(asset.video.is_some());
assert!(asset.data.is_none());
}
#[test]
fn test_asset_with_data_only() {
let data = Data::builder().type_(1).build().unwrap();
let asset = Asset::builder().id(1).data(Some(data)).build().unwrap();
assert!(asset.title.is_none());
assert!(asset.img.is_none());
assert!(asset.video.is_none());
assert!(asset.data.is_some());
}
#[test]
fn test_asset_deserialization_with_multiple_types() {
let json = r#"{
"id": 1,
"title": {"len": 90},
"img": {"w": 300, "h": 250}
}"#;
let result: Result<Asset, _> = serde_json::from_str(json);
assert!(
result.is_ok(),
"Asset with multiple types in JSON currently deserializes"
);
let asset = result.unwrap();
assert!(asset.title.is_some());
assert!(asset.img.is_some());
}
#[test]
fn test_asset_deserialization_with_no_types() {
let json = r#"{"id": 1}"#;
let result: Result<Asset, _> = serde_json::from_str(json);
assert!(result.is_ok(), "Asset with no types currently deserializes");
let asset = result.unwrap();
assert!(asset.title.is_none());
assert!(asset.img.is_none());
assert!(asset.video.is_none());
assert!(asset.data.is_none());
}
#[test]
fn test_native_request_default() {
let request: NativeRequest = NativeRequest::default();
assert_eq!(request.ver, None);
assert_eq!(request.context, None);
assert_eq!(request.contextsubtype, None);
assert_eq!(request.plcmttype, None);
assert_eq!(request.plcmtcnt, None);
assert_eq!(request.seq, None);
assert!(request.assets.is_empty());
assert_eq!(request.aurlsupport, None);
assert_eq!(request.durlsupport, None);
assert_eq!(request.eventtrackers, None);
assert_eq!(request.privacy, None);
assert_eq!(request.ext, None);
}
#[test]
fn test_asset_default() {
let asset: Asset = Asset::default();
assert_eq!(asset.id, 0);
assert_eq!(asset.required, None);
assert_eq!(asset.title, None);
assert_eq!(asset.img, None);
assert_eq!(asset.video, None);
assert_eq!(asset.data, None);
assert_eq!(asset.ext, None);
}
#[test]
fn test_title_default() {
let title: Title = Title::default();
assert_eq!(title.len, 0);
assert_eq!(title.ext, None);
}
#[test]
fn test_image_default() {
let image: Image = Image::default();
assert_eq!(image.type_, None);
assert_eq!(image.w, None);
assert_eq!(image.h, None);
assert_eq!(image.wmin, None);
assert_eq!(image.hmin, None);
assert_eq!(image.wmax, None);
assert_eq!(image.hmax, None);
assert_eq!(image.mimes, None);
assert_eq!(image.ext, None);
}
#[test]
fn test_video_default() {
let video: Video = Video::default();
assert!(video.mimes.is_empty());
assert_eq!(video.minduration, None);
assert_eq!(video.maxduration, None);
assert_eq!(video.protocols, None);
assert_eq!(video.ext, None);
}
#[test]
fn test_data_default() {
let data: Data = Data::default();
assert_eq!(data.type_, 0);
assert_eq!(data.len, None);
assert_eq!(data.ext, None);
}
#[test]
fn test_event_tracker_default() {
let tracker: EventTracker = EventTracker::default();
assert_eq!(tracker.event, 0);
assert!(tracker.methods.is_empty());
assert_eq!(tracker.ext, None);
}
#[test]
fn test_title_serde_roundtrip() {
let original = Title::builder().len(90).build().unwrap();
let json = serde_json::to_string(&original).unwrap();
let parsed: Title = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_image_full_serde_roundtrip() {
let original = Image::builder()
.type_(Some(3))
.w(Some(1200))
.h(Some(627))
.wmin(Some(600))
.hmin(Some(314))
.wmax(Some(1920))
.hmax(Some(1080))
.mimes(Some(vec![
"image/jpeg".to_string(),
"image/png".to_string(),
]))
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
let parsed: Image = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_video_full_serde_roundtrip() {
let original = 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();
let json = serde_json::to_string(&original).unwrap();
let parsed: Video = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_data_full_serde_roundtrip() {
let original = Data::builder().type_(2).len(Some(140)).build().unwrap();
let json = serde_json::to_string(&original).unwrap();
let parsed: Data = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_event_tracker_serde_roundtrip() {
let original = EventTracker::builder()
.event(1)
.methods(vec![1, 2])
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
let parsed: EventTracker = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_asset_with_title_serde_roundtrip() {
let original = Asset::builder()
.id(1)
.required(Some(1))
.title(Some(Title::builder().len(90).build().unwrap()))
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
let parsed: Asset = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_asset_with_img_serde_roundtrip() {
let original = Asset::builder()
.id(2)
.required(Some(1))
.img(Some(
Image::builder()
.type_(Some(3))
.w(Some(1200))
.h(Some(627))
.build()
.unwrap(),
))
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
let parsed: Asset = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_asset_with_video_serde_roundtrip() {
let original = Asset::builder()
.id(3)
.required(Some(1))
.video(Some(
Video::builder()
.mimes(vec!["video/mp4".to_string()])
.minduration(Some(5))
.maxduration(Some(30))
.protocols(Some(vec![2, 3]))
.build()
.unwrap(),
))
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
let parsed: Asset = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_asset_with_data_serde_roundtrip() {
let original = Asset::builder()
.id(4)
.required(Some(0))
.data(Some(
Data::builder().type_(2).len(Some(140)).build().unwrap(),
))
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
let parsed: Asset = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_native_request_full_serde_roundtrip() {
let original = NativeRequest::builder()
.ver("1.2")
.context(Some(1))
.contextsubtype(Some(10))
.plcmttype(Some(1))
.plcmtcnt(Some(3))
.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)
.required(Some(1))
.img(Some(
Image::builder()
.type_(Some(3))
.w(Some(1200))
.h(Some(627))
.wmin(Some(600))
.hmin(Some(314))
.mimes(Some(vec!["image/jpeg".to_string()]))
.build()
.unwrap(),
))
.build()
.unwrap(),
Asset::builder()
.id(3)
.required(Some(0))
.video(Some(
Video::builder()
.mimes(vec!["video/mp4".to_string()])
.minduration(Some(5))
.maxduration(Some(30))
.protocols(Some(vec![2, 3, 5, 6]))
.build()
.unwrap(),
))
.build()
.unwrap(),
Asset::builder()
.id(4)
.required(Some(0))
.data(Some(
Data::builder().type_(2).len(Some(140)).build().unwrap(),
))
.build()
.unwrap(),
])
.aurlsupport(Some(0))
.durlsupport(Some(1))
.eventtrackers(Some(vec![
EventTracker::builder()
.event(1)
.methods(vec![1, 2])
.build()
.unwrap(),
EventTracker::builder()
.event(2)
.methods(vec![1])
.build()
.unwrap(),
]))
.privacy(Some(1))
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
let parsed: NativeRequest = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_realistic_ssp_native_request_deserialization() {
let json = r#"{
"ver": "1.2",
"context": 1,
"contextsubtype": 10,
"plcmttype": 1,
"plcmtcnt": 1,
"seq": 0,
"assets": [
{
"id": 1,
"required": 1,
"title": {
"len": 90
}
},
{
"id": 2,
"required": 1,
"img": {
"type": 1,
"w": 50,
"h": 50,
"wmin": 30,
"hmin": 30,
"mimes": ["image/png", "image/jpeg"]
}
},
{
"id": 3,
"required": 1,
"img": {
"type": 3,
"w": 1200,
"h": 627,
"wmin": 600,
"hmin": 314,
"wmax": 1920,
"hmax": 1080,
"mimes": ["image/jpeg", "image/png", "image/gif"]
}
},
{
"id": 4,
"required": 0,
"video": {
"mimes": ["video/mp4", "video/webm"],
"minduration": 5,
"maxduration": 30,
"protocols": [2, 3, 5, 6]
}
},
{
"id": 5,
"required": 1,
"data": {
"type": 1,
"len": 25
}
},
{
"id": 6,
"required": 0,
"data": {
"type": 2,
"len": 140
}
},
{
"id": 7,
"required": 0,
"data": {
"type": 12,
"len": 15
}
}
],
"aurlsupport": 0,
"durlsupport": 0,
"eventtrackers": [
{
"event": 1,
"methods": [1, 2]
},
{
"event": 2,
"methods": [1]
}
],
"privacy": 1
}"#;
let request: NativeRequest = serde_json::from_str(json).unwrap();
assert_eq!(request.ver, Some("1.2".to_string()));
assert_eq!(request.context, Some(1));
assert_eq!(request.contextsubtype, Some(10));
assert_eq!(request.plcmttype, Some(1));
assert_eq!(request.plcmtcnt, Some(1));
assert_eq!(request.seq, Some(0));
assert_eq!(request.aurlsupport, Some(0));
assert_eq!(request.durlsupport, Some(0));
assert_eq!(request.privacy, Some(1));
assert_eq!(request.assets.len(), 7);
let a1 = &request.assets[0];
assert_eq!(a1.id, 1);
assert_eq!(a1.required, Some(1));
assert_eq!(a1.title.as_ref().unwrap().len, 90);
let a2 = &request.assets[1];
assert_eq!(a2.id, 2);
let img2 = a2.img.as_ref().unwrap();
assert_eq!(img2.type_, Some(1));
assert_eq!(img2.w, Some(50));
assert_eq!(img2.h, Some(50));
let a3 = &request.assets[2];
assert_eq!(a3.id, 3);
let img3 = a3.img.as_ref().unwrap();
assert_eq!(img3.type_, Some(3));
assert_eq!(img3.wmax, Some(1920));
assert_eq!(img3.hmax, Some(1080));
assert_eq!(img3.mimes.as_ref().unwrap().len(), 3);
let a4 = &request.assets[3];
assert_eq!(a4.id, 4);
assert_eq!(a4.required, Some(0));
let vid = a4.video.as_ref().unwrap();
assert_eq!(vid.mimes.len(), 2);
assert_eq!(vid.minduration, Some(5));
assert_eq!(vid.maxduration, Some(30));
assert_eq!(vid.protocols, Some(vec![2, 3, 5, 6]));
let a5 = &request.assets[4];
assert_eq!(a5.data.as_ref().unwrap().type_, 1);
assert_eq!(a5.data.as_ref().unwrap().len, Some(25));
let a6 = &request.assets[5];
assert_eq!(a6.data.as_ref().unwrap().type_, 2);
let a7 = &request.assets[6];
assert_eq!(a7.data.as_ref().unwrap().type_, 12);
let trackers = request.eventtrackers.as_ref().unwrap();
assert_eq!(trackers.len(), 2);
assert_eq!(trackers[0].event, 1);
assert_eq!(trackers[0].methods, vec![1, 2]);
assert_eq!(trackers[1].event, 2);
assert_eq!(trackers[1].methods, vec![1]);
}
#[test]
fn test_contextsubtype_builder_and_serde_roundtrip() {
let request = NativeRequest::builder()
.context(Some(1))
.contextsubtype(Some(11))
.assets(vec![])
.build()
.unwrap();
assert_eq!(request.contextsubtype, Some(11));
let json = serde_json::to_string(&request).unwrap();
let parsed: NativeRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.contextsubtype, Some(11));
}
#[test]
fn test_image_wmax_hmax_serde_roundtrip() {
let original = Image::builder()
.wmin(Some(300))
.hmin(Some(200))
.wmax(Some(1920))
.hmax(Some(1080))
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
assert!(json.contains(r#""wmax":1920"#));
assert!(json.contains(r#""hmax":1080"#));
let parsed: Image = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_video_protocols_individual_roundtrip() {
for protocol in [2, 3, 5, 6, 7] {
let original = Video::builder()
.mimes(vec!["video/mp4".to_string()])
.protocols(Some(vec![protocol]))
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
let parsed: Video = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.protocols, Some(vec![protocol]));
}
}
#[test]
fn test_event_tracker_full_serde_roundtrip() {
let original = EventTracker::builder()
.event(3) .methods(vec![1, 2]) .build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
assert!(json.contains(r#""event":3"#));
assert!(json.contains(r#""methods":[1,2]"#));
let parsed: EventTracker = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_seq_field_serde_roundtrip() {
let original = NativeRequest::builder()
.seq(Some(5))
.assets(vec![])
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
assert!(json.contains(r#""seq":5"#));
let parsed: NativeRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.seq, Some(5));
}
#[test]
fn test_plcmtcnt_field_serde_roundtrip() {
let original = NativeRequest::builder()
.plcmtcnt(Some(10))
.assets(vec![])
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
assert!(json.contains(r#""plcmtcnt":10"#));
let parsed: NativeRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.plcmtcnt, Some(10));
}
#[test]
fn test_aurlsupport_durlsupport_serde_roundtrip() {
let original = NativeRequest::builder()
.aurlsupport(Some(1))
.durlsupport(Some(1))
.assets(vec![])
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
assert!(json.contains(r#""aurlsupport":1"#));
assert!(json.contains(r#""durlsupport":1"#));
let parsed: NativeRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.aurlsupport, Some(1));
assert_eq!(parsed.durlsupport, Some(1));
}
#[test]
fn test_privacy_field_serde_roundtrip() {
let original = NativeRequest::builder()
.privacy(Some(1))
.assets(vec![])
.build()
.unwrap();
let json = serde_json::to_string(&original).unwrap();
assert!(json.contains(r#""privacy":1"#));
let parsed: NativeRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.privacy, Some(1));
}
}