add tests for broadcasts
This commit is contained in:
@@ -1,22 +1,44 @@
|
|||||||
use crate::Backend;
|
use crate::Backend;
|
||||||
use chrono::DateTime;
|
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 {
|
impl Backend {
|
||||||
pub(crate) async fn get_broadcast(
|
pub(crate) async fn get_broadcast(
|
||||||
&self,
|
&self,
|
||||||
url: String,
|
url: String,
|
||||||
) -> Result<Option<Broadcast>, Box<dyn std::error::Error>> {
|
) -> Result<Option<Broadcast>, Box<dyn std::error::Error>> {
|
||||||
let data: Value = match self {
|
let data: InferenceData = match self {
|
||||||
Backend::Prod => reqwest::get(&url).await?.json().await?,
|
Backend::Prod => reqwest::get(&url).await?.json::<InferenceData>().await?,
|
||||||
|
|
||||||
#[cfg(test)]
|
#[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
|
Broadcast::from_data(url, data).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct Broadcast {
|
pub(crate) struct Broadcast {
|
||||||
pub(crate) url: String,
|
pub(crate) url: String,
|
||||||
pub(crate) media_url: String,
|
pub(crate) media_url: String,
|
||||||
@@ -27,32 +49,23 @@ pub(crate) struct Broadcast {
|
|||||||
impl Broadcast {
|
impl Broadcast {
|
||||||
async fn from_data(
|
async fn from_data(
|
||||||
url: String,
|
url: String,
|
||||||
data: Value,
|
data: InferenceData,
|
||||||
) -> Result<Option<Self>, Box<dyn std::error::Error>> {
|
) -> Result<Option<Self>, Box<dyn std::error::Error>> {
|
||||||
let Some(streams) = data["streams"].as_array() else {
|
if data.streams.is_empty() {
|
||||||
return Err(String::from("No 'streams' found").into());
|
|
||||||
};
|
|
||||||
if streams.is_empty() {
|
|
||||||
return Ok(None);
|
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 {
|
let media_url = format!(
|
||||||
return Err(String::from("No 'loopStreamId' found").into());
|
"https://loopstream01.apa.at/?channel=oe1&shoutcast=0&id={}",
|
||||||
};
|
stream.loop_stream_id
|
||||||
let media_url = format!("https://loopstream01.apa.at/?channel=oe1&shoutcast=0&id={id}");
|
);
|
||||||
|
|
||||||
let Some(title) = data["title"].as_str() else {
|
let Some(timestamp) = DateTime::from_timestamp(stream.start_timestamp / 1000, 0) 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 {
|
|
||||||
return Err(format!(
|
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());
|
.into());
|
||||||
};
|
};
|
||||||
@@ -60,7 +73,7 @@ impl Broadcast {
|
|||||||
Ok(Some(Self {
|
Ok(Some(Self {
|
||||||
url,
|
url,
|
||||||
media_url,
|
media_url,
|
||||||
title: title.into(),
|
title: data.title,
|
||||||
timestamp,
|
timestamp,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -71,3 +84,29 @@ impl PartialEq for Broadcast {
|
|||||||
self.url == other.url
|
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]
|
#[tokio::test]
|
||||||
async fn filter_result() {
|
async fn filter_result() {
|
||||||
let backend = Backend::Test;
|
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"]);
|
assert_eq!(actual, vec!["test-href"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user