Add a SQLite database backend #265
@ -13,7 +13,7 @@ pub enum AlbumLibIdDef {
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub struct SerdeAlbumLibId(#[serde(with = "AlbumLibIdDef")] AlbumLibId);
|
||||
|
||||
impl From<SerdeAlbumLibId> for AlbumLibId {
|
||||
@ -36,7 +36,7 @@ pub struct AlbumDateDef {
|
||||
day: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub struct SerdeAlbumDate(#[serde(with = "AlbumDateDef")] pub AlbumDate);
|
||||
|
||||
impl From<SerdeAlbumDate> for AlbumDate {
|
||||
@ -69,7 +69,7 @@ pub enum AlbumPrimaryTypeDef {
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub struct SerdeAlbumPrimaryType(#[serde(with = "AlbumPrimaryTypeDef")] AlbumPrimaryType);
|
||||
|
||||
impl From<SerdeAlbumPrimaryType> for AlbumPrimaryType {
|
||||
@ -101,7 +101,7 @@ pub enum AlbumSecondaryTypeDef {
|
||||
FieldRecording,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub struct SerdeAlbumSecondaryType(#[serde(with = "AlbumSecondaryTypeDef")] AlbumSecondaryType);
|
||||
|
||||
impl From<SerdeAlbumSecondaryType> for AlbumSecondaryType {
|
||||
|
@ -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 name: &'a str,
|
||||
pub sort: Option<&'a str>,
|
||||
@ -31,7 +31,7 @@ pub struct SerializeArtist<'a> {
|
||||
pub albums: Vec<SerializeAlbum<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Serialize, PartialEq, Eq)]
|
||||
pub struct SerializeAlbum<'a> {
|
||||
pub title: &'a str,
|
||||
pub lib_id: SerdeAlbumLibId,
|
||||
@ -42,12 +42,12 @@ pub struct SerializeAlbum<'a> {
|
||||
pub secondary_types: Vec<SerdeAlbumSecondaryType>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Serialize, PartialEq, Eq)]
|
||||
pub struct SerializeMbRefOption<'a>(
|
||||
#[serde(with = "MbRefOptionDef")] MbRefOption<SerializeMbid<'a>>,
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SerializeMbid<'a>(&'a Mbid);
|
||||
|
||||
impl<'a, T: IMusicBrainzRef> From<&'a MbRefOption<T>> for SerializeMbRefOption<'a> {
|
||||
|
@ -144,9 +144,9 @@ impl<SDB: for<'conn> ISqlDatabaseBackend<'conn>> SqlDatabase<SDB> {
|
||||
}
|
||||
|
||||
fn drop_tables<Tx: ISqlTransactionBackend>(tx: &Tx) -> Result<(), Error> {
|
||||
tx.drop_database_metadata_table()?;
|
||||
tx.drop_artists_table()?;
|
||||
tx.drop_albums_table()?;
|
||||
tx.drop_artists_table()?;
|
||||
tx.drop_database_metadata_table()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -201,103 +201,186 @@ impl<SDB: for<'conn> ISqlDatabaseBackend<'conn>> IDatabase for SqlDatabase<SDB>
|
||||
// #[cfg(test)]
|
||||
// pub mod testmod;
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use std::collections::HashMap;
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// use std::collections::HashMap;
|
||||
|
||||
// use mockall::predicate;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
// use crate::core::{
|
||||
// collection::{artist::Artist, Collection},
|
||||
// testmod::FULL_COLLECTION,
|
||||
// };
|
||||
use mockall::{predicate, Sequence};
|
||||
|
||||
// use super::*;
|
||||
// use testmod::DATABASE_JSON;
|
||||
use crate::core::{
|
||||
// collection::{artist::Artist, Collection},
|
||||
testmod::FULL_COLLECTION,
|
||||
};
|
||||
|
||||
// fn expected() -> Collection {
|
||||
// let mut expected = FULL_COLLECTION.to_owned();
|
||||
// for artist in expected.iter_mut() {
|
||||
// for album in artist.albums.iter_mut() {
|
||||
// album.tracks.clear();
|
||||
// }
|
||||
// }
|
||||
// expected
|
||||
// }
|
||||
use super::*;
|
||||
|
||||
// #[test]
|
||||
// fn save() {
|
||||
// let write_data = FULL_COLLECTION.to_owned();
|
||||
// let input = DATABASE_JSON.to_owned();
|
||||
// fn expected() -> Collection {
|
||||
// let mut expected = FULL_COLLECTION.to_owned();
|
||||
// for artist in expected.iter_mut() {
|
||||
// for album in artist.albums.iter_mut() {
|
||||
// album.tracks.clear();
|
||||
// }
|
||||
// }
|
||||
// expected
|
||||
// }
|
||||
|
||||
// let mut backend = MockISqlDatabaseBackend::new();
|
||||
// backend
|
||||
// .expect_write()
|
||||
// .with(predicate::eq(input))
|
||||
// .times(1)
|
||||
// .return_once(|_| Ok(()));
|
||||
struct SqlDatabaseTestBackend {
|
||||
pub txs: VecDeque<MockISqlTransactionBackend>,
|
||||
}
|
||||
|
||||
// SqlDatabase::new(backend).save(&write_data).unwrap();
|
||||
// }
|
||||
impl SqlDatabaseTestBackend {
|
||||
fn new(txs: VecDeque<MockISqlTransactionBackend>) -> Self {
|
||||
SqlDatabaseTestBackend { txs }
|
||||
}
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn load() {
|
||||
// let expected = expected();
|
||||
// let result = Ok(DATABASE_JSON.to_owned());
|
||||
// eprintln!("{DATABASE_JSON}");
|
||||
impl ISqlDatabaseBackend<'_> for SqlDatabaseTestBackend {
|
||||
type Tx = MockISqlTransactionBackend;
|
||||
|
||||
// let mut backend = MockISqlDatabaseBackend::new();
|
||||
// backend.expect_read().times(1).return_once(|| result);
|
||||
fn transaction(&mut self) -> Result<Self::Tx, Error> {
|
||||
Ok(self.txs.pop_front().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
// let read_data: Vec<Artist> = SqlDatabase::new(backend).load().unwrap();
|
||||
macro_rules! then {
|
||||
($tx:ident, $seq:ident, $expect:ident) => {
|
||||
$tx.$expect().times(1).in_sequence(&mut $seq)
|
||||
};
|
||||
}
|
||||
|
||||
// assert_eq!(read_data, expected);
|
||||
// }
|
||||
macro_rules! then0 {
|
||||
($tx:ident, $seq:ident, $expect:ident) => {
|
||||
then!($tx, $seq, $expect).return_once(|| Ok(()))
|
||||
};
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn reverse() {
|
||||
// let input = DATABASE_JSON.to_owned();
|
||||
// let result = Ok(input.clone());
|
||||
macro_rules! then1 {
|
||||
($tx:ident, $seq:ident, $expect:ident) => {
|
||||
then!($tx, $seq, $expect).return_once(|_| Ok(()))
|
||||
};
|
||||
}
|
||||
|
||||
// let mut backend = MockISqlDatabaseBackend::new();
|
||||
// backend
|
||||
// .expect_write()
|
||||
// .with(predicate::eq(input))
|
||||
// .times(1)
|
||||
// .return_once(|_| Ok(()));
|
||||
// backend.expect_read().times(1).return_once(|| result);
|
||||
// let mut database = SqlDatabase::new(backend);
|
||||
macro_rules! then2 {
|
||||
($tx:ident, $seq:ident, $expect:ident) => {
|
||||
then!($tx, $seq, $expect).return_once(|_, _| Ok(()))
|
||||
};
|
||||
}
|
||||
|
||||
// let write_data = FULL_COLLECTION.to_owned();
|
||||
// database.save(&write_data).unwrap();
|
||||
// let read_data: Vec<Artist> = database.load().unwrap();
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
// // Album information is not saved to disk.
|
||||
// let expected = expected();
|
||||
// assert_eq!(read_data, expected);
|
||||
// }
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn load_errors() {
|
||||
// let json = String::from("");
|
||||
// let serde_err = serde_json::from_str::<DeserializeDatabase>(&json);
|
||||
// assert!(serde_err.is_err());
|
||||
fn database(txs: VecDeque<MockISqlTransactionBackend>) -> SqlDatabase<SqlDatabaseTestBackend> {
|
||||
let mut backend = SqlDatabaseTestBackend::new(txs);
|
||||
let mut tx = MockISqlTransactionBackend::new();
|
||||
|
||||
// let serde_err: LoadError = serde_err.unwrap_err().into();
|
||||
// assert!(!serde_err.to_string().is_empty());
|
||||
// assert!(!format!("{:?}", serde_err).is_empty());
|
||||
// }
|
||||
let mut seq = Sequence::new();
|
||||
expect_create!(tx, seq);
|
||||
then0!(tx, seq, expect_commit);
|
||||
|
||||
// #[test]
|
||||
// fn save_errors() {
|
||||
// // serde_json will raise an error as it has certain requirements on keys.
|
||||
// let mut object = HashMap::<Result<(), ()>, String>::new();
|
||||
// object.insert(Ok(()), String::from("string"));
|
||||
// let serde_err = serde_json::to_string(&object);
|
||||
// assert!(serde_err.is_err());
|
||||
backend.txs.push_front(tx);
|
||||
SqlDatabase::new(backend).unwrap()
|
||||
}
|
||||
|
||||
// let serde_err: SaveError = serde_err.unwrap_err().into();
|
||||
// assert!(!serde_err.to_string().is_empty());
|
||||
// assert!(!format!("{:?}", serde_err).is_empty());
|
||||
// }
|
||||
// }
|
||||
#[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]
|
||||
// fn load() {
|
||||
// let expected = expected();
|
||||
// let result = Ok(DATABASE_JSON.to_owned());
|
||||
// eprintln!("{DATABASE_JSON}");
|
||||
|
||||
// let mut backend = MockISqlDatabaseBackend::new();
|
||||
// backend.expect_read().times(1).return_once(|| result);
|
||||
|
||||
// let read_data: Vec<Artist> = SqlDatabase::new(backend).load().unwrap();
|
||||
|
||||
// assert_eq!(read_data, expected);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn reverse() {
|
||||
// let input = DATABASE_JSON.to_owned();
|
||||
// let result = Ok(input.clone());
|
||||
|
||||
// let mut backend = MockISqlDatabaseBackend::new();
|
||||
// backend
|
||||
// .expect_write()
|
||||
// .with(predicate::eq(input))
|
||||
// .times(1)
|
||||
// .return_once(|_| Ok(()));
|
||||
// backend.expect_read().times(1).return_once(|| result);
|
||||
// let mut database = SqlDatabase::new(backend);
|
||||
|
||||
// let write_data = FULL_COLLECTION.to_owned();
|
||||
// database.save(&write_data).unwrap();
|
||||
// let read_data: Vec<Artist> = database.load().unwrap();
|
||||
|
||||
// // Album information is not saved to disk.
|
||||
// let expected = expected();
|
||||
// assert_eq!(read_data, expected);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn load_errors() {
|
||||
// let json = String::from("");
|
||||
// let serde_err = serde_json::from_str::<DeserializeDatabase>(&json);
|
||||
// assert!(serde_err.is_err());
|
||||
|
||||
// let serde_err: LoadError = serde_err.unwrap_err().into();
|
||||
// assert!(!serde_err.to_string().is_empty());
|
||||
// assert!(!format!("{:?}", serde_err).is_empty());
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn save_errors() {
|
||||
// // serde_json will raise an error as it has certain requirements on keys.
|
||||
// let mut object = HashMap::<Result<(), ()>, String>::new();
|
||||
// object.insert(Ok(()), String::from("string"));
|
||||
// let serde_err = serde_json::to_string(&object);
|
||||
// assert!(serde_err.is_err());
|
||||
|
||||
// let serde_err: SaveError = serde_err.unwrap_err().into();
|
||||
// assert!(!serde_err.to_string().is_empty());
|
||||
// assert!(!format!("{:?}", serde_err).is_empty());
|
||||
// }
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user