Add unit tests and url checking

This commit is contained in:
Wojciech Kozlowski 2023-05-21 12:00:01 +02:00
parent c8c259ab7a
commit 97c9080d50

View File

@ -44,6 +44,20 @@ pub trait IUuid {
fn uuid(&self) -> &str;
}
#[derive(Debug, PartialEq, Eq)]
enum UrlType {
MusicBrainz,
MusicButler,
Bandcamp,
Qobuz,
}
#[derive(Debug, PartialEq, Eq)]
struct InvalidUrlError {
url_type: UrlType,
url: String,
}
/// MusicBrainz reference.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct MusicBrainz {
@ -54,18 +68,25 @@ pub struct MusicBrainz {
impl MusicBrainz {
pub fn new<S: AsRef<str>>(url: S) -> Result<Self, Error> {
let url = Url::parse(url.as_ref())?;
let mbid: Mbid = match url.path_segments().and_then(|mut ps| ps.nth(2)) {
if !url.domain().map(|u| u.ends_with("musicbrainz.org")).unwrap_or(false) {
return Err(Self::invalid_url_error(url).into())
}
let mbid: Mbid = match url.path_segments().and_then(|mut ps| ps.nth(1)) {
Some(segment) => Mbid::new(segment)?,
None => {
return Err(Error::UrlParseError(format!(
"invalid MusicBrainz URL: {}",
url.as_str()
)))
}
None => return Err(Self::invalid_url_error(url).into()),
};
Ok(MusicBrainz { url, mbid })
}
fn invalid_url_error<S: Into<String>>(url: S) -> InvalidUrlError {
InvalidUrlError {
url_type: UrlType::MusicBrainz,
url: url.into(),
}
}
}
impl IUrl for MusicBrainz {
@ -89,8 +110,20 @@ pub struct MusicButler {
impl MusicButler {
pub fn new<S: AsRef<str>>(url: S) -> Result<Self, Error> {
let url = Url::parse(url.as_ref())?;
if !url.domain().map(|u| u.ends_with("musicbutler.io")).unwrap_or(false) {
return Err(Self::invalid_url_error(url).into())
}
Ok(MusicButler { url })
}
fn invalid_url_error<S: Into<String>>(url: S) -> InvalidUrlError {
InvalidUrlError {
url_type: UrlType::MusicButler,
url: url.into(),
}
}
}
impl IUrl for MusicButler {
@ -108,8 +141,20 @@ pub struct Bandcamp {
impl Bandcamp {
pub fn new<S: AsRef<str>>(url: S) -> Result<Self, Error> {
let url = Url::parse(url.as_ref())?;
if !url.domain().map(|u| u.ends_with("bandcamp.com")).unwrap_or(false) {
return Err(Self::invalid_url_error(url).into())
}
Ok(Bandcamp { url })
}
fn invalid_url_error<S: Into<String>>(url: S) -> InvalidUrlError {
InvalidUrlError {
url_type: UrlType::Bandcamp,
url: url.into(),
}
}
}
impl IUrl for Bandcamp {
@ -127,8 +172,20 @@ pub struct Qobuz {
impl Qobuz {
pub fn new<S: AsRef<str>>(url: S) -> Result<Self, Error> {
let url = Url::parse(url.as_ref())?;
if !url.domain().map(|u| u.ends_with("qobuz.com")).unwrap_or(false) {
return Err(Self::invalid_url_error(url).into())
}
Ok(Qobuz { url })
}
fn invalid_url_error<S: Into<String>>(url: S) -> InvalidUrlError {
InvalidUrlError {
url_type: UrlType::Qobuz,
url: url.into(),
}
}
}
impl IUrl for Qobuz {
@ -323,6 +380,8 @@ pub enum Error {
DatabaseError(String),
/// The [`MusicHoard`] failed to parse a user-provided URL.
UrlParseError(String),
/// The user-provided URL is not valid.
InvalidUrlError(String),
}
impl fmt::Display for Error {
@ -333,6 +392,7 @@ impl fmt::Display for Error {
write!(f, "failed to read/write from/to the database: {s}")
}
Self::UrlParseError(ref s) => write!(f, "failed to parse a user-provided URL: {s}"),
Self::InvalidUrlError(ref s) => write!(f, "user-provided URL is invalid: {s}"),
}
}
}
@ -367,6 +427,12 @@ impl From<uuid::Error> for Error {
}
}
impl From<InvalidUrlError> for Error {
fn from(err: InvalidUrlError) -> Error {
Error::InvalidUrlError(format!("{:?} - {}", err.url_type, err.url))
}
}
/// The Music Hoard. It is responsible for pulling information from both the library and the
/// database, ensuring its consistent and writing back any changes.
pub struct MusicHoard<LIB, DB> {
@ -545,6 +611,49 @@ mod tests {
items
}
#[test]
fn musicbrainz() {
let uuid = "d368baa8-21ca-4759-9731-0b2753071ad8";
let url = format!("https://musicbrainz.org/artist/{uuid}");
let mb = MusicBrainz::new(&url).unwrap();
assert_eq!(url, mb.url());
assert_eq!(uuid, mb.uuid());
let url = format!("https://musicbrainz.org/artist/i-am-not-a-uuid");
assert!(MusicBrainz::new(&url).is_err());
let url = format!("https://musicbrainz.org/artist");
assert!(MusicBrainz::new(&url).is_err());
}
#[test]
fn urls() {
let musicbrainz = "https://musicbrainz.org/artist/d368baa8-21ca-4759-9731-0b2753071ad8";
let musicbutler = "https://www.musicbutler.io/artist-page/483340948";
let bandcamp = "https://thelasthangmen.bandcamp.com/";
let qobuz = "https://www.qobuz.com/nl-nl/interpreter/the-last-hangmen/1244413";
assert!(MusicBrainz::new(&musicbrainz).is_ok());
assert!(MusicBrainz::new(&musicbutler).is_err());
assert!(MusicBrainz::new(&bandcamp).is_err());
assert!(MusicBrainz::new(&qobuz).is_err());
assert!(MusicButler::new(&musicbrainz).is_err());
assert!(MusicButler::new(&musicbutler).is_ok());
assert!(MusicButler::new(&bandcamp).is_err());
assert!(MusicButler::new(&qobuz).is_err());
assert!(Bandcamp::new(&musicbrainz).is_err());
assert!(Bandcamp::new(&musicbutler).is_err());
assert!(Bandcamp::new(&bandcamp).is_ok());
assert!(Bandcamp::new(&qobuz).is_err());
assert!(Qobuz::new(&musicbrainz).is_err());
assert!(Qobuz::new(&musicbutler).is_err());
assert!(Qobuz::new(&bandcamp).is_err());
assert!(Qobuz::new(&qobuz).is_ok());
}
#[test]
fn merge_track() {
let left = Track {