add tests for broadcasts
This commit is contained in:
		| @@ -1,22 +1,44 @@ | ||||
| use crate::Backend; | ||||
| use chrono::DateTime; | ||||
| use serde_json::Value; | ||||
| use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| struct InferenceStream { | ||||
|     #[serde(rename = "loopStreamId")] | ||||
|     pub loop_stream_id: String, | ||||
|     #[serde(rename = "start")] | ||||
|     pub start_timestamp: i64, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| struct InferenceData { | ||||
|     pub title: String, | ||||
|     pub streams: Vec<InferenceStream>, | ||||
| } | ||||
|  | ||||
| impl Backend { | ||||
|     pub(crate) async fn get_broadcast( | ||||
|         &self, | ||||
|         url: String, | ||||
|     ) -> Result<Option<Broadcast>, Box<dyn std::error::Error>> { | ||||
|         let data: Value = match self { | ||||
|             Backend::Prod => reqwest::get(&url).await?.json().await?, | ||||
|         let data: InferenceData = match self { | ||||
|             Backend::Prod => reqwest::get(&url).await?.json::<InferenceData>().await?, | ||||
|  | ||||
|             #[cfg(test)] | ||||
|             Backend::Test => todo!(), | ||||
|             Backend::Test => InferenceData { | ||||
|                 title: "test-title".into(), | ||||
|                 streams: vec![InferenceStream { | ||||
|                     loop_stream_id: "test.mp3".into(), | ||||
|                     start_timestamp: 1761734636000, | ||||
|                 }], | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
|         Broadcast::from_data(url, data).await | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| #[derive(Debug, Clone)] | ||||
| pub(crate) struct Broadcast { | ||||
|     pub(crate) url: String, | ||||
|     pub(crate) media_url: String, | ||||
| @@ -27,32 +49,23 @@ pub(crate) struct Broadcast { | ||||
| impl Broadcast { | ||||
|     async fn from_data( | ||||
|         url: String, | ||||
|         data: Value, | ||||
|         data: InferenceData, | ||||
|     ) -> Result<Option<Self>, Box<dyn std::error::Error>> { | ||||
|         let Some(streams) = data["streams"].as_array() else { | ||||
|             return Err(String::from("No 'streams' found").into()); | ||||
|         }; | ||||
|         if streams.is_empty() { | ||||
|         if data.streams.is_empty() { | ||||
|             return Ok(None); | ||||
|         } | ||||
|         assert_eq!(streams.len(), 1); | ||||
|         assert_eq!(data.streams.len(), 1); | ||||
|         let stream = &data.streams[0]; | ||||
|  | ||||
|         let Some(id) = streams[0]["loopStreamId"].as_str() else { | ||||
|             return Err(String::from("No 'loopStreamId' found").into()); | ||||
|         }; | ||||
|         let media_url = format!("https://loopstream01.apa.at/?channel=oe1&shoutcast=0&id={id}"); | ||||
|         let media_url = format!( | ||||
|             "https://loopstream01.apa.at/?channel=oe1&shoutcast=0&id={}", | ||||
|             stream.loop_stream_id | ||||
|         ); | ||||
|  | ||||
|         let Some(title) = data["title"].as_str() else { | ||||
|             return Err(format!("{url} has no title").into()); | ||||
|         }; | ||||
|  | ||||
|         let Some(timestamp) = data["start"].as_number() else { | ||||
|             return Err(format!("{url} has no start").into()); | ||||
|         }; | ||||
|         let Some(timestamp) = DateTime::from_timestamp(timestamp.as_i64().unwrap() / 1000, 0) | ||||
|         else { | ||||
|         let Some(timestamp) = DateTime::from_timestamp(stream.start_timestamp / 1000, 0) else { | ||||
|             return Err(format!( | ||||
|                 "broadcastDay in {url} not in a valid format (unix timestamp): {timestamp}" | ||||
|                 "broadcastDay in {url} not in a valid format (unix timestamp): {}", | ||||
|                 stream.start_timestamp | ||||
|             ) | ||||
|             .into()); | ||||
|         }; | ||||
| @@ -60,7 +73,7 @@ impl Broadcast { | ||||
|         Ok(Some(Self { | ||||
|             url, | ||||
|             media_url, | ||||
|             title: title.into(), | ||||
|             title: data.title, | ||||
|             timestamp, | ||||
|         })) | ||||
|     } | ||||
| @@ -71,3 +84,29 @@ impl PartialEq for Broadcast { | ||||
|         self.url == other.url | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::Backend; | ||||
|     use chrono::{TimeZone, Utc}; | ||||
|  | ||||
|     #[tokio::test] | ||||
|     async fn happy() { | ||||
|         let backend = Backend::Test; | ||||
|         let actual = backend | ||||
|             .get_broadcast("test-url".into()) | ||||
|             .await | ||||
|             .unwrap() | ||||
|             .unwrap(); | ||||
|         assert_eq!(&actual.url, "test-url"); | ||||
|         assert_eq!( | ||||
|             &actual.media_url, | ||||
|             "https://loopstream01.apa.at/?channel=oe1&shoutcast=0&id=test.mp3" | ||||
|         ); | ||||
|         assert_eq!(&actual.title, "test-title"); | ||||
|         assert_eq!( | ||||
|             actual.timestamp, | ||||
|             Utc.with_ymd_and_hms(2025, 10, 29, 10, 43, 56).unwrap() | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -89,7 +89,10 @@ mod tests { | ||||
|     #[tokio::test] | ||||
|     async fn filter_result() { | ||||
|         let backend = Backend::Test; | ||||
|         let actual = backend.get_broadcasts(&["test-href".into()]).await.unwrap(); | ||||
|         let actual = backend | ||||
|             .get_broadcasts(&["test-title".into()]) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|         assert_eq!(actual, vec!["test-href"]); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Philipp Hofer
					Philipp Hofer