// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html #![cfg(test)] use std::{collections::HashSet, fs::File, io::Write}; use crate::{ media::files::sha1_of_data, prelude::*, search::SearchNode, tests::open_fs_test_collection, }; const SAMPLE_JPG: &str = "sample.jpg"; const SAMPLE_MP3: &str = "sample.mp3"; const SAMPLE_JS: &str = "_sample.js"; const JPG_DATA: &[u8] = b"1"; const MP3_DATA: &[u8] = b"2"; const JS_DATA: &[u8] = b"3"; const OTHER_MP3_DATA: &[u8] = b"4"; #[test] fn roundtrip() { let (mut src_col, src_tempdir) = open_fs_test_collection("src"); let (mut target_col, _target_tempdir) = open_fs_test_collection("target"); let apkg_path = src_tempdir.path().join("test.apkg"); let (main_deck, sibling_deck) = src_col.add_sample_decks(); let notetype = src_col.add_sample_notetype(); let note = src_col.add_sample_note(&main_deck, &sibling_deck, ¬etype); src_col.add_sample_media(); target_col.add_conflicting_media(); src_col .export_apkg( &apkg_path, SearchNode::from_deck_name("parent::sample"), true, true, None, |_| (), ) .unwrap(); target_col.import_apkg(&apkg_path, &mut |_| Ok(())).unwrap(); target_col.assert_decks(); target_col.assert_notetype(¬etype); target_col.assert_note_and_media(¬e); target_col.undo().unwrap(); target_col.assert_empty(); } impl Collection { fn add_sample_decks(&mut self) -> (Deck, Deck) { let sample = self.add_named_deck("parent\x1fsample"); self.add_named_deck("parent\x1fsample\x1fchild"); let siblings = self.add_named_deck("siblings"); (sample, siblings) } fn add_named_deck(&mut self, name: &str) -> Deck { let mut deck = Deck::new_normal(); deck.name = NativeDeckName::from_native_str(name); self.add_deck(&mut deck).unwrap(); deck } fn add_sample_notetype(&mut self) -> Notetype { let mut nt = Notetype { name: "sample".into(), ..Default::default() }; nt.add_field("sample"); nt.add_template("sample1", "{{sample}}", ""); nt.add_template("sample2", "{{sample}}2", ""); self.add_notetype(&mut nt, true).unwrap(); nt } fn add_sample_note( &mut self, main_deck: &Deck, sibling_decks: &Deck, notetype: &Notetype, ) -> Note { let mut sample = notetype.new_note(); sample.fields_mut()[0] = format!(" [sound:{SAMPLE_MP3}]"); sample.tags = vec!["sample".into()]; self.add_note(&mut sample, main_deck.id).unwrap(); let card = self .storage .get_card_by_ordinal(sample.id, 1) .unwrap() .unwrap(); self.set_deck(&[card.id], sibling_decks.id).unwrap(); sample } fn add_sample_media(&self) { self.add_media(&[ (SAMPLE_JPG, JPG_DATA), (SAMPLE_MP3, MP3_DATA), (SAMPLE_JS, JS_DATA), ]); } fn add_conflicting_media(&mut self) { let mut file = File::create(self.media_folder.join(SAMPLE_MP3)).unwrap(); file.write_all(OTHER_MP3_DATA).unwrap(); } fn assert_decks(&mut self) { let existing_decks: HashSet<_> = self .get_all_deck_names(true) .unwrap() .into_iter() .map(|(_, name)| name) .collect(); for deck in ["parent", "parent::sample", "siblings"] { assert!(existing_decks.contains(deck)); } assert!(!existing_decks.contains("parent::sample::child")); } fn assert_notetype(&mut self, notetype: &Notetype) { assert!(self.get_notetype(notetype.id).unwrap().is_some()); } fn assert_note_and_media(&mut self, note: &Note) { let sha1 = sha1_of_data(MP3_DATA); let new_mp3_name = format!("sample-{}.mp3", hex::encode(&sha1)); for file in [SAMPLE_JPG, SAMPLE_JS, &new_mp3_name] { assert!(self.media_folder.join(file).exists()) } let imported_note = self.storage.get_note(note.id).unwrap().unwrap(); assert!(imported_note.fields()[0].contains(&new_mp3_name)); } fn assert_empty(&self) { assert!(self.get_all_deck_names(true).unwrap().is_empty()); assert!(self.storage.get_all_note_ids().unwrap().is_empty()); assert!(self.storage.get_all_card_ids().unwrap().is_empty()); assert!(self.storage.all_tags().unwrap().is_empty()); } }