From bf044bd7c8521fe6eef061ed6950eeb157989242 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Fri, 4 Aug 2023 07:50:07 +0100 Subject: [PATCH] create failing test for run --- Cargo.lock | 23 +++++++++++++++ Cargo.toml | 1 + src/errors.rs | 7 +++++ src/lib.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++ src/test_utils.rs | 26 +++++++++++------ 5 files changed, 120 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aba38b1..04cc534 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -325,6 +325,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "diligent-date-parser" version = "0.1.4" @@ -1016,6 +1022,7 @@ dependencies = [ "atom_syndication", "bytes", "clap", + "pretty_assertions", "reqwest", "scraper", "tempfile", @@ -1033,6 +1040,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro2" version = "1.0.66" @@ -1726,3 +1743,9 @@ checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi", ] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/Cargo.toml b/Cargo.toml index a01c713..3c17ea9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ bytes = "1.4.0" [dev-dependencies] tempfile = "*" +pretty_assertions = "*" diff --git a/src/errors.rs b/src/errors.rs index 70fed6e..d8f4236 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -37,6 +37,13 @@ impl From for Error { } } } +impl From> for Error { + fn from(value: std::sync::mpsc::SendError) -> Self { + Self { + details: value.to_string(), + } + } +} impl From for Error { fn from(value: atom_syndication::Error) -> Self { Self { diff --git a/src/lib.rs b/src/lib.rs index a890a5f..52cd075 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,12 +37,84 @@ pub fn run(subscriptions: &str, history: &str, site: &str, e: Env) -> Result<()> #[cfg(test)] mod tests { + use std::{collections::HashMap, sync::mpsc}; + + use atom_syndication::{Entry, EntryBuilder, Feed, FeedBuilder, LinkBuilder, Text}; + + use crate::test_utils::{ + create_text_file, mock_fetch_as_text_with_rss_url, mock_file_append_line, mock_file_open, + mock_network_download_as_mp3, mock_network_fetch_as_bytes_with_rss_entries, + }; + use pretty_assertions::assert_eq; + use super::*; + #[test] fn downloads_two_items_from_two_feeds_ignoring_existing_items_in_history() -> Result<()> { //given + + let site = "http://example.com/"; + let (tx, rx) = mpsc::channel::(); // channel to recieve notice of downloaded urls + + // two channels in subscriptions.txt + let (subs_dir, subs_file_name) = + create_text_file("subs", "@channel1\nignore me\n@channel2".as_bytes())?; + // one item from each channel is already listed in the downloads.txt file + let (history_dir, history_file_name) = + create_text_file("history", "c1-f2\nc2-f3".as_bytes())?; + + let env = Env { + network: NetworkEnv { + fetch_as_text: mock_fetch_as_text_with_rss_url(HashMap::from([ + ("http://example.com/@channel1", "rss-feed-1"), + ("http://example.com/@channel2", "rss-feed-2"), + ])), + fetch_as_bytes: mock_network_fetch_as_bytes_with_rss_entries(HashMap::from([ + ( + "rss-feed-1".into(), + feed_with_three_links("c1-f1", "c1-f2", "c1-f3").to_string(), + ), + ( + "rss-feed-2".into(), + feed_with_three_links("c2-f1", "c2-f2", "c2-f3").to_string(), + ), + ])), + download_as_mp3: mock_network_download_as_mp3(tx), + }, + file: FileEnv { + open: mock_file_open(vec![subs_file_name.clone()]), + append_line: mock_file_append_line(), + }, + }; + //when + run(&subs_file_name, &history_file_name, site, env)?; //then + drop(subs_dir); + drop(history_dir); + + let mut downloads: Vec = vec![]; + for m in rx { + downloads.push(m); + } + + assert_eq!(downloads, vec!["c1-f1", "c1-f3", "c2-f1", "c2-f2"]); Ok(()) } + + fn entry_with_link(link: &str, title: &str) -> Entry { + EntryBuilder::default() + .links(vec![LinkBuilder::default().href(link.to_string()).build()]) + .title(Text::from(title)) + .build() + } + fn feed_with_three_links(l1: &str, l2: &str, l3: &str) -> Feed { + FeedBuilder::default() + .entries(vec![ + entry_with_link(l1, "l1"), + entry_with_link(l2, "l2"), + entry_with_link(l3, "l3"), + ]) + .build() + } } diff --git a/src/test_utils.rs b/src/test_utils.rs index ca5286c..5afc9d5 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -3,6 +3,7 @@ use std::{ fs::{read_to_string, File}, io::Write, str::from_utf8, + sync::mpsc::Sender, }; use tempfile::{tempdir, TempDir}; @@ -69,8 +70,15 @@ pub fn mock_file_open(real_paths: Vec) -> FileOpenFn { }) } -pub fn mock_network_download_as_mp3() -> NetworkDownloadAsMp3Fn { - Box::new(|_url| Ok(())) +pub fn mock_network_download_as_mp3(tx: Sender) -> NetworkDownloadAsMp3Fn { + Box::new(move |url: &str| { + tx.send(url.into())?; + Ok(()) + }) +} + +pub fn mock_file_append_line() -> FileAppendLineFn { + Box::new(|_, _| Ok(())) } pub fn stub_network_fetch_as_bytes() -> NetworkFetchAsBytesFn { @@ -94,10 +102,10 @@ pub fn stub_network_download_as_mp3() -> NetworkDownloadAsMp3Fn { // )) // }) // } -pub fn stub_file_append_line() -> FileAppendLineFn { - Box::new(|path: &str, line: &str| { - Err(Error::message( - format!("Not implemented: file_append_line: {} to {}", line, path).as_str(), - )) - }) -} +// pub fn stub_file_append_line() -> FileAppendLineFn { +// Box::new(|path: &str, line: &str| { +// Err(Error::message( +// format!("Not implemented: file_append_line: {} to {}", line, path).as_str(), +// )) +// }) +// }