diff options
author | D. Scott Boggs <scott@tams.tech> | 2023-02-12 12:30:11 -0500 |
---|---|---|
committer | D. Scott Boggs <scott@tams.tech> | 2024-04-08 08:56:16 -0400 |
commit | aa94354c0f1b88455cf6d7ee3de79cecfbc30ade (patch) | |
tree | 247a3dd7620441e079eec7c6aaab96d180fb604c | |
parent | bf359f5e7edf58c14746debea4753e0c2a321121 (diff) |
Add Admin::Measure example test
-rw-r--r-- | entities/src/admin/measure.rs | 70 | ||||
-rw-r--r-- | entities/src/conversion.rs | 53 |
2 files changed, 120 insertions, 3 deletions
diff --git a/entities/src/admin/measure.rs b/entities/src/admin/measure.rs index 7d93d6c..08a39bc 100644 --- a/entities/src/admin/measure.rs +++ b/entities/src/admin/measure.rs @@ -14,13 +14,14 @@ pub struct Measure { #[serde(with = "conversion::string_to_u64")] pub total: u64, /// A human-readable formatted value for this data item. - pub human_value: String, + #[serde(default)] + pub human_value: Option<String>, /// The numeric total associated with the requested measure, in the previous /// period. Previous period is calculated by subtracting the start_at and /// end_at dates, then offsetting both start and end dates backwards by the /// length of the time period. - #[serde(with = "conversion::string_to_u64")] - pub previous_total: u64, + #[serde(with = "conversion::string_to_u64::option")] + pub previous_total: Option<u64>, /// The data available for the requested measure, split into daily buckets. pub data: Vec<Data>, } @@ -35,3 +36,66 @@ pub struct Data { #[serde(with = "conversion::string_to_u64")] pub value: u64, } + +#[cfg(test)] +mod tests { + use time::Month; + + use super::*; + + #[test] + fn test_measure_example() { + let example = r#"{ + "key": "active_users", + "unit": null, + "total": "2", + "previous_total": "0", + "data": [ + { + "date": "2022-09-14T00:00:00Z", + "value": "0" + }, + { + "date": "2022-09-15T00:00:00Z", + "value": "0" + }, + { + "date": "2022-09-16T00:00:00Z", + "value": "0" + }, + { + "date": "2022-09-17T00:00:00Z", + "value": "1" + }, + { + "date": "2022-09-18T00:00:00Z", + "value": "1" + }, + { + "date": "2022-09-19T00:00:00Z", + "value": "1" + }, + { + "date": "2022-09-20T00:00:00Z", + "value": "2" + }, + { + "date": "2022-09-21T00:00:00Z", + "value": "1" + } + ] + }"#; + let subject: Measure = serde_json::from_str(example).unwrap(); + assert_eq!(subject.key, MeasureKey::new("active_users")); + assert!(subject.unit.is_none()); + assert_eq!(subject.total, 2); + assert_eq!(subject.previous_total, Some(0)); + assert!(subject.human_value.is_none()); + let data = &subject.data[0]; + assert_eq!(data.value, 0); + let date = data.date.date(); + assert_eq!(date.year(), 2022); + assert_eq!(date.month(), Month::September); + assert_eq!(date.day(), 14); + } +}
\ No newline at end of file diff --git a/entities/src/conversion.rs b/entities/src/conversion.rs index 04ae3b9..ccfaa81 100644 --- a/entities/src/conversion.rs +++ b/entities/src/conversion.rs @@ -36,6 +36,59 @@ pub(crate) mod string_to_u64 { deserializer.deserialize_str(StringToIntVisitor) } + + pub(crate) mod option { + use serde::{ + de::{self, Visitor}, + Deserializer, Serializer, + }; + + pub(crate) fn serialize<S>(value: &Option<u64>, ser: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + if let Some(value) = value { + ser.serialize_str(&value.to_string()) + } else { + ser.serialize_none() + } + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error> + where + D: Deserializer<'de>, + { + struct StringToIntVisitor; + + impl<'v> Visitor<'v> for StringToIntVisitor { + type Value = Option<u64>; + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + formatter, + "a string which can be parsed as an unsigned, 64-bit integer" + ) + } + fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + let v: u64 = v + .parse() + .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(v), &self))?; + Ok(Some(v)) + } + + fn visit_none<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + } + + deserializer.deserialize_any(StringToIntVisitor) + } + } } pub(crate) mod maybe_empty_url { |