From 544ed5271275f0c59e08e74a89bd1788ecc1a3cd Mon Sep 17 00:00:00 2001 From: SuperCuber Date: Sat, 30 Jan 2021 16:18:29 +0200 Subject: [PATCH] Add some more methods to Filesystem, make first test --- src/actions.rs | 6 +-- src/deploy.rs | 102 +++++++++++++++++++++++++++++++++++++++++++--- src/file_state.rs | 72 -------------------------------- src/filesystem.rs | 15 ++++++- 4 files changed, 113 insertions(+), 82 deletions(-) diff --git a/src/actions.rs b/src/actions.rs index 39360f6..f53dbc0 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -2,8 +2,6 @@ use anyhow::{Context, Result}; use crossterm::style::Colorize; use handlebars::Handlebars; -use std::fs; - use crate::args::Options; use crate::config::Variables; use crate::difference; @@ -476,7 +474,7 @@ pub(crate) fn perform_template_deploy( variables: &Variables, ) -> Result<()> { let file_contents = - fs::read_to_string(&template.source).context("read template source file")?; + fs.read_to_string(&template.source).context("read template source file")?; let file_contents = template.apply_actions(file_contents); let rendered = handlebars .render_template(&file_contents, variables) @@ -491,7 +489,7 @@ pub(crate) fn perform_template_deploy( &None, ) .context("create parent for cache file")?; - fs::write(&template.cache, rendered).context("write rendered template to cache")?; + fs.write(&template.cache, rendered).context("write rendered template to cache")?; // Target fs.copy_file( diff --git a/src/deploy.rs b/src/deploy.rs index bd06e99..bde8655 100644 --- a/src/deploy.rs +++ b/src/deploy.rs @@ -239,15 +239,27 @@ pub fn plan_deploy(state: FileState) -> Vec { #[cfg(test)] mod test { - use crate::config::{SymbolicTarget, TemplateTarget}; - use crate::file_state::{SymlinkDescription, TemplateDescription}; + use crate::{ + config::{SymbolicTarget, TemplateTarget}, + filesystem::SymlinkComparison, + }; + use crate::{ + file_state::{SymlinkDescription, TemplateDescription}, + filesystem::TemplateComparison, + }; - use std::collections::BTreeSet; + use std::{ + collections::BTreeSet, + path::{Path, PathBuf}, + }; use super::*; + use mockall::predicate::*; + #[test] fn initial_deploy() { + // File state let a = SymlinkDescription { source: "a_in".into(), target: SymbolicTarget { @@ -276,15 +288,95 @@ mod test { existing_templates: BTreeSet::new(), }; + // Plan let actions = plan_deploy(file_state); assert_eq!( actions, [Action::CreateSymlink(a), Action::CreateTemplate(b)] ); + // Setup let mut fs = crate::filesystem::MockFilesystem::new(); - fs.expect_remove_file().times(1).returning(|_p| Ok(())); + let mut seq = mockall::Sequence::new(); + + let options = Options::default(); + let handlebars = handlebars::Handlebars::new(); + let variables = Default::default(); + + fn path_eq(expected: &str) -> impl Fn(&Path) -> bool { + let expected = PathBuf::from(expected); + move |actual| actual == expected + } - actions[0].run(&mut fs, &Options::default(), todo!(), todo!()).unwrap(); + // Action 1 + fs.expect_compare_symlink() + .times(1) + .with(function(path_eq("a_in")), function(path_eq("a_out"))) + .in_sequence(&mut seq) + .returning(|_, _| Ok(SymlinkComparison::OnlySourceExists)); + fs.expect_create_dir_all() + .times(1) + .with(function(path_eq("")), eq(None)) // parent of a_out + .in_sequence(&mut seq) + .returning(|_, _| Ok(())); + fs.expect_make_symlink() + .times(1) + .with( + function(path_eq("a_out")), + function(path_eq("a_in")), + eq(None), + ) + .in_sequence(&mut seq) + .returning(|_, _, _| Ok(())); + + actions[0] + .run(&mut fs, &options, &handlebars, &variables) + .unwrap(); + + fs.checkpoint(); + + // Action 2 + fs.expect_compare_template() + .times(1) + .with( + function(path_eq("b_out")), + function(path_eq("cache/b_cache")), + ) + .in_sequence(&mut seq) + .returning(|_, _| Ok(TemplateComparison::BothMissing)); + fs.expect_create_dir_all() + .times(1) + .with(function(path_eq("")), eq(None)) // parent of b_out + .in_sequence(&mut seq) + .returning(|_, _| Ok(())); + fs.expect_read_to_string() + .times(1) + .with(function(path_eq("b_in"))) + .in_sequence(&mut seq) + .returning(|_| Ok("".into())); + fs.expect_create_dir_all() + .times(1) + .with(function(path_eq("cache")), eq(None)) + .in_sequence(&mut seq) + .returning(|_, _| Ok(())); + fs.expect_write() + .times(1) + .with(function(path_eq("cache/b_cache")), eq(String::from(""))) + .in_sequence(&mut seq) + .returning(|_, _| Ok(())); + fs.expect_copy_file() + .times(1) + .with(function(path_eq("cache/b_cache")), function(path_eq("b_out")), eq(None)) + .in_sequence(&mut seq) + .returning(|_, _, _| Ok(())); + fs.expect_copy_permissions() + .times(1) + .with(function(path_eq("b_in")), function(path_eq("b_out")), eq(None)) + .in_sequence(&mut seq) + .returning(|_, _, _| Ok(())); + + actions[1] + .run(&mut fs, &options, &handlebars, &variables) + .unwrap(); } } diff --git a/src/file_state.rs b/src/file_state.rs index be5a5cb..9afb02b 100644 --- a/src/file_state.rs +++ b/src/file_state.rs @@ -238,75 +238,3 @@ impl FileState { .collect() } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_file_state_symlinks_only() { - let mut existing_symlinks = BTreeMap::new(); - existing_symlinks.insert("file1s".into(), "file1t".into()); // Same - existing_symlinks.insert("file2s".into(), "file2t".into()); // Deleted - existing_symlinks.insert("file3s".into(), "file3t".into()); // Target change - - let mut desired_symlinks = BTreeMap::new(); - desired_symlinks.insert("file1s".into(), "file1t".into()); // Same - desired_symlinks.insert("file3s".into(), "file0t".into()); // Target change - desired_symlinks.insert("file5s".into(), "file5t".into()); // New - - let state = FileState::new( - desired_symlinks, - Default::default(), - existing_symlinks, - Default::default(), - "cache", - ); - - assert_eq!( - state.deleted_files(), - ( - vec![ - SymlinkDescription { - source: "file2s".into(), - target: "file2t".into(), - }, - SymlinkDescription { - source: "file3s".into(), - target: "file3t".into(), - } - ], - Vec::new() - ), - "deleted files correct" - ); - assert_eq!( - state.new_files(), - ( - vec![ - SymlinkDescription { - source: "file3s".into(), - target: "file0t".into(), - }, - SymlinkDescription { - source: "file5s".into(), - target: "file5t".into(), - }, - ], - Vec::new() - ), - "new files correct" - ); - assert_eq!( - state.old_files(), - ( - vec![SymlinkDescription { - source: "file1s".into(), - target: "file1t".into(), - }], - Vec::new() - ), - "old files correct" - ); - } -} diff --git a/src/filesystem.rs b/src/filesystem.rs index 9256cae..b4fce16 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -69,6 +69,12 @@ pub trait Filesystem { /// Removes a file or folder, elevating privileges if needed fn remove_file(&mut self, path: &Path) -> Result<()>; + /// Read contents of file into a string + fn read_to_string(&mut self, path: &Path) -> Result; + + /// Write string to file, without elevating privileges + fn write(&mut self, path: &Path, content: String) -> Result<()>; + /// Delete parents of target file if they're empty fn delete_parents(&mut self, path: &Path) -> Result<()>; @@ -183,6 +189,14 @@ impl Filesystem for RealFilesystem { } } + fn read_to_string(&mut self, path: &Path) -> Result { + fs::read_to_string(path).context("read from file") + } + + fn write(&mut self, path: &Path, content: String) -> Result<()> { + fs::write(path, content).context("write to file") + } + fn delete_parents(&mut self, path: &Path) -> Result<()> { let mut path = path.parent().context("get parent")?; while path.is_dir() @@ -636,7 +650,6 @@ mod filesystem_impl { pub fn platform_dunce(path: &Path) -> PathBuf { path.into() } - } pub use self::filesystem_impl::*;