Succinct unit tests
Some checks failed
Cargo CI / Build and Test (pull_request) Failing after 2m15s
Cargo CI / Lint (pull_request) Successful in 1m7s

This commit is contained in:
Wojciech Kozlowski 2025-01-11 20:44:47 +01:00
parent 03d950109b
commit e24f776896
3 changed files with 174 additions and 91 deletions

View File

@ -13,7 +13,7 @@ pub enum AlbumLibIdDef {
None, None,
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct SerdeAlbumLibId(#[serde(with = "AlbumLibIdDef")] AlbumLibId); pub struct SerdeAlbumLibId(#[serde(with = "AlbumLibIdDef")] AlbumLibId);
impl From<SerdeAlbumLibId> for AlbumLibId { impl From<SerdeAlbumLibId> for AlbumLibId {
@ -36,7 +36,7 @@ pub struct AlbumDateDef {
day: Option<u8>, day: Option<u8>,
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct SerdeAlbumDate(#[serde(with = "AlbumDateDef")] pub AlbumDate); pub struct SerdeAlbumDate(#[serde(with = "AlbumDateDef")] pub AlbumDate);
impl From<SerdeAlbumDate> for AlbumDate { impl From<SerdeAlbumDate> for AlbumDate {
@ -69,7 +69,7 @@ pub enum AlbumPrimaryTypeDef {
Other, Other,
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct SerdeAlbumPrimaryType(#[serde(with = "AlbumPrimaryTypeDef")] AlbumPrimaryType); pub struct SerdeAlbumPrimaryType(#[serde(with = "AlbumPrimaryTypeDef")] AlbumPrimaryType);
impl From<SerdeAlbumPrimaryType> for AlbumPrimaryType { impl From<SerdeAlbumPrimaryType> for AlbumPrimaryType {
@ -101,7 +101,7 @@ pub enum AlbumSecondaryTypeDef {
FieldRecording, FieldRecording,
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct SerdeAlbumSecondaryType(#[serde(with = "AlbumSecondaryTypeDef")] AlbumSecondaryType); pub struct SerdeAlbumSecondaryType(#[serde(with = "AlbumSecondaryTypeDef")] AlbumSecondaryType);
impl From<SerdeAlbumSecondaryType> for AlbumSecondaryType { impl From<SerdeAlbumSecondaryType> for AlbumSecondaryType {

View File

@ -22,7 +22,7 @@ impl<'a> From<&'a Collection> for SerializeDatabase<'a> {
} }
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct SerializeArtist<'a> { pub struct SerializeArtist<'a> {
pub name: &'a str, pub name: &'a str,
pub sort: Option<&'a str>, pub sort: Option<&'a str>,
@ -31,7 +31,7 @@ pub struct SerializeArtist<'a> {
pub albums: Vec<SerializeAlbum<'a>>, pub albums: Vec<SerializeAlbum<'a>>,
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct SerializeAlbum<'a> { pub struct SerializeAlbum<'a> {
pub title: &'a str, pub title: &'a str,
pub lib_id: SerdeAlbumLibId, pub lib_id: SerdeAlbumLibId,
@ -42,12 +42,12 @@ pub struct SerializeAlbum<'a> {
pub secondary_types: Vec<SerdeAlbumSecondaryType>, pub secondary_types: Vec<SerdeAlbumSecondaryType>,
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct SerializeMbRefOption<'a>( pub struct SerializeMbRefOption<'a>(
#[serde(with = "MbRefOptionDef")] MbRefOption<SerializeMbid<'a>>, #[serde(with = "MbRefOptionDef")] MbRefOption<SerializeMbid<'a>>,
); );
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct SerializeMbid<'a>(&'a Mbid); pub struct SerializeMbid<'a>(&'a Mbid);
impl<'a, T: IMusicBrainzRef> From<&'a MbRefOption<T>> for SerializeMbRefOption<'a> { impl<'a, T: IMusicBrainzRef> From<&'a MbRefOption<T>> for SerializeMbRefOption<'a> {

View File

@ -144,9 +144,9 @@ impl<SDB: for<'conn> ISqlDatabaseBackend<'conn>> SqlDatabase<SDB> {
} }
fn drop_tables<Tx: ISqlTransactionBackend>(tx: &Tx) -> Result<(), Error> { fn drop_tables<Tx: ISqlTransactionBackend>(tx: &Tx) -> Result<(), Error> {
tx.drop_database_metadata_table()?;
tx.drop_artists_table()?;
tx.drop_albums_table()?; tx.drop_albums_table()?;
tx.drop_artists_table()?;
tx.drop_database_metadata_table()?;
Ok(()) Ok(())
} }
} }
@ -201,19 +201,20 @@ impl<SDB: for<'conn> ISqlDatabaseBackend<'conn>> IDatabase for SqlDatabase<SDB>
// #[cfg(test)] // #[cfg(test)]
// pub mod testmod; // pub mod testmod;
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use std::collections::HashMap; // use std::collections::HashMap;
// use mockall::predicate; use std::collections::VecDeque;
// use crate::core::{ use mockall::{predicate, Sequence};
use crate::core::{
// collection::{artist::Artist, Collection}, // collection::{artist::Artist, Collection},
// testmod::FULL_COLLECTION, testmod::FULL_COLLECTION,
// }; };
// use super::*; use super::*;
// use testmod::DATABASE_JSON;
// fn expected() -> Collection { // fn expected() -> Collection {
// let mut expected = FULL_COLLECTION.to_owned(); // let mut expected = FULL_COLLECTION.to_owned();
@ -225,20 +226,102 @@ impl<SDB: for<'conn> ISqlDatabaseBackend<'conn>> IDatabase for SqlDatabase<SDB>
// expected // expected
// } // }
// #[test] struct SqlDatabaseTestBackend {
// fn save() { pub txs: VecDeque<MockISqlTransactionBackend>,
// let write_data = FULL_COLLECTION.to_owned(); }
// let input = DATABASE_JSON.to_owned();
// let mut backend = MockISqlDatabaseBackend::new(); impl SqlDatabaseTestBackend {
// backend fn new(txs: VecDeque<MockISqlTransactionBackend>) -> Self {
// .expect_write() SqlDatabaseTestBackend { txs }
// .with(predicate::eq(input)) }
// .times(1) }
// .return_once(|_| Ok(()));
// SqlDatabase::new(backend).save(&write_data).unwrap(); impl ISqlDatabaseBackend<'_> for SqlDatabaseTestBackend {
// } type Tx = MockISqlTransactionBackend;
fn transaction(&mut self) -> Result<Self::Tx, Error> {
Ok(self.txs.pop_front().unwrap())
}
}
macro_rules! then {
($tx:ident, $seq:ident, $expect:ident) => {
$tx.$expect().times(1).in_sequence(&mut $seq)
};
}
macro_rules! then0 {
($tx:ident, $seq:ident, $expect:ident) => {
then!($tx, $seq, $expect).return_once(|| Ok(()))
};
}
macro_rules! then1 {
($tx:ident, $seq:ident, $expect:ident) => {
then!($tx, $seq, $expect).return_once(|_| Ok(()))
};
}
macro_rules! then2 {
($tx:ident, $seq:ident, $expect:ident) => {
then!($tx, $seq, $expect).return_once(|_, _| Ok(()))
};
}
macro_rules! expect_create {
($tx:ident, $seq:ident) => {
let mut seq = Sequence::new();
then0!($tx, seq, expect_create_database_metadata_table);
then0!($tx, seq, expect_create_artists_table);
then0!($tx, seq, expect_create_albums_table);
};
}
macro_rules! expect_drop {
($tx:ident, $seq:ident) => {
let mut seq = Sequence::new();
then0!($tx, seq, expect_drop_albums_table);
then0!($tx, seq, expect_drop_artists_table);
then0!($tx, seq, expect_drop_database_metadata_table);
};
}
fn database(txs: VecDeque<MockISqlTransactionBackend>) -> SqlDatabase<SqlDatabaseTestBackend> {
let mut backend = SqlDatabaseTestBackend::new(txs);
let mut tx = MockISqlTransactionBackend::new();
let mut seq = Sequence::new();
expect_create!(tx, seq);
then0!(tx, seq, expect_commit);
backend.txs.push_front(tx);
SqlDatabase::new(backend).unwrap()
}
#[test]
fn save() {
let write_data = FULL_COLLECTION.to_owned();
let mut tx = MockISqlTransactionBackend::new();
let mut seq = Sequence::new();
expect_drop!(tx, seq);
expect_create!(tx, seq);
then1!(tx, seq, expect_insert_database_version).with(predicate::eq(V20250103));
for artist in write_data.iter() {
let ac = artist.clone();
then1!(tx, seq, expect_insert_artist)
.withf(move |a| a == &Into::<SerializeArtist>::into(&ac));
for album in artist.albums.iter() {
let (nc, ac) = (artist.meta.id.name.clone(), album.clone());
then2!(tx, seq, expect_insert_album)
.withf(move |n, a| n == nc && a == &Into::<SerializeAlbum>::into(&ac));
}
}
then0!(tx, seq, expect_commit);
assert!(database(VecDeque::from([tx])).save(&write_data).is_ok());
}
// #[test] // #[test]
// fn load() { // fn load() {
@ -300,4 +383,4 @@ impl<SDB: for<'conn> ISqlDatabaseBackend<'conn>> IDatabase for SqlDatabase<SDB>
// assert!(!serde_err.to_string().is_empty()); // assert!(!serde_err.to_string().is_empty());
// assert!(!format!("{:?}", serde_err).is_empty()); // assert!(!format!("{:?}", serde_err).is_empty());
// } // }
// } }