aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command/add.rs95
-rw-r--r--src/command/help.rs47
-rw-r--r--src/command/update.rs223
-rw-r--r--src/remote/git.rs120
-rw-r--r--src/remote/mod.rs64
5 files changed, 455 insertions, 94 deletions
diff --git a/src/command/add.rs b/src/command/add.rs
index d38d077..9622f81 100644
--- a/src/command/add.rs
+++ b/src/command/add.rs
@@ -48,3 +48,98 @@ impl super::Command for Add {
"<path_to_post>\t\t\tCreates new blog post"
}
}
+
+#[cfg(test)]
+mod tests {
+ use std::fs::create_dir_all;
+
+ use super::*;
+ use crate::command::Command;
+ use crate::configuration::Configuration;
+
+ use test_utilities::*;
+
+ #[test]
+ fn test_add_command() {
+ let add = Add::new();
+
+ // Create all directories
+ let test_dir = setup_test_dir();
+
+ let posts_dir = test_dir.join("posts");
+ create_dir_all(&posts_dir).expect("Could not create posts test directory");
+ create_dir_all(&posts_dir.join("0")).expect("Could not create post 0 test directory");
+
+ // Create Test Files
+
+ // Create configuration
+ let mut configuration = Configuration::new();
+ configuration.posts_directory = posts_dir.clone();
+
+ // Let's ensure the initial state is OK
+ assert!(&posts_dir.join("0").exists());
+ assert!(!&posts_dir.join("1").exists());
+ assert!(!&posts_dir.join("2").exists());
+ assert!(!&posts_dir.join("3").exists());
+
+ // Add 1 of 3
+ add.execute(None, &configuration, "add")
+ .expect("Could not add a.gmi");
+ assert!(!&posts_dir.join("0").exists());
+ assert!(&posts_dir.join("1").exists());
+ assert!(!&posts_dir.join("2").exists());
+ assert!(!&posts_dir.join("3").exists());
+
+ // Add 2 of 3
+ add.execute(None, &configuration, "add")
+ .expect("Could not add a.gmi");
+ assert!(!&posts_dir.join("0").exists());
+ assert!(!&posts_dir.join("1").exists());
+ assert!(&posts_dir.join("2").exists());
+ assert!(!&posts_dir.join("3").exists());
+
+ // Add 3 of 3
+ add.execute(None, &configuration, "add")
+ .expect("Could not add a.gmi");
+ assert!(!&posts_dir.join("0").exists());
+ assert!(!&posts_dir.join("1").exists());
+ assert!(!&posts_dir.join("2").exists());
+ assert!(!&posts_dir.join("3").exists());
+
+ cleanup_test_dir(&test_dir);
+ }
+
+ #[test]
+ fn add_before_dependencies() {
+ let add = Add::new();
+ let dependencies = add.before_dependencies();
+
+ assert_eq!(dependencies.len(), 1);
+ assert_eq!(dependencies[0].command(), "sync-down");
+ }
+
+ #[test]
+ fn add_after_dependencies() {
+ let add = Add::new();
+ let dependencies = add.after_dependencies();
+
+ assert_eq!(dependencies.len(), 3);
+ assert_eq!(dependencies[0].command(), "update");
+ assert_eq!(dependencies[1].command(), "generate");
+ assert_eq!(dependencies[2].command(), "sync-up");
+ }
+
+ // These two tests feel pointless but I'm doing it for the coverage :p
+
+ #[test]
+ fn add_command_output() {
+ let add = Add::new();
+ add.command();
+ }
+
+ #[test]
+ fn add_help_output() {
+ let add = Add::new();
+ add.help();
+ }
+}
diff --git a/src/command/help.rs b/src/command/help.rs
index b682fc0..da5831c 100644
--- a/src/command/help.rs
+++ b/src/command/help.rs
@@ -38,3 +38,50 @@ impl super::Command for Help {
"\t\t\t\tPrints this help"
}
}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+ use crate::command::Command;
+ use crate::configuration::Configuration;
+
+ #[test]
+ fn test_add_command() {
+ let help = Help::new();
+
+ let configuration = Configuration::new();
+ help.execute(None, &configuration, "")
+ .expect("Could not call help");
+ }
+
+ #[test]
+ fn help_before_dependencies() {
+ let help = Help::new();
+ let dependencies = help.before_dependencies();
+
+ assert_eq!(dependencies.len(), 0);
+ }
+
+ #[test]
+ fn help_after_dependencies() {
+ let help = Help::new();
+ let dependencies = help.after_dependencies();
+
+ assert_eq!(dependencies.len(), 0);
+ }
+
+ // These two tests feel pointless but I'm doing it for the coverage :p
+
+ #[test]
+ fn help_command_output() {
+ let help = Help::new();
+ help.command();
+ }
+
+ #[test]
+ fn help_help_output() {
+ let help = Help::new();
+ help.help();
+ }
+}
diff --git a/src/command/update.rs b/src/command/update.rs
index 54005f8..fedec13 100644
--- a/src/command/update.rs
+++ b/src/command/update.rs
@@ -40,6 +40,7 @@ impl Update {
let entry_target = target.join(entry_name);
if entry_type.is_dir() {
+ create_dir_all(&entry_target)?;
Update::archive(&entry_source, &entry_target)?;
} else {
copy(&entry_source, &entry_target)?;
@@ -110,3 +111,225 @@ impl super::Command for Update {
"<path_to_post>\t\tUpdates latest blog post"
}
}
+
+#[cfg(test)]
+mod tests {
+ use std::fs::{create_dir_all, read_dir};
+ use std::path::PathBuf;
+
+ use super::*;
+ use crate::command::Command;
+ use crate::configuration::Configuration;
+
+ use test_utilities::*;
+
+ #[test]
+ fn test_update_command() {
+ let update = Update::new();
+
+ // Create all directories
+ let test_dir = setup_test_dir();
+
+ // Reference but don't create the directories
+ let posts_dir = test_dir.join("posts");
+ let archive_dir = test_dir.join("archive");
+
+ // Create configuration
+ let mut configuration = Configuration::new();
+ configuration.posts_directory = posts_dir.clone();
+ configuration.archive_directory = archive_dir.clone();
+
+ // Create Test File
+ let test_file = test_dir.join("cool-test-file-😎.gmi");
+ let test_file_string = test_file.display().to_string();
+ create_test_file(&test_file, "# I try.");
+
+ // Update 1 of 2
+ update.execute(Some(&test_file_string), &configuration, "update")
+ .expect("Could not update a .gmi");
+ assert!(&posts_dir.join("0").exists());
+ assert_file_contents(&posts_dir.join("0/cool-test-file-😎.gmi"), "# I try.");
+
+ let entries = read_dir(&archive_dir)
+ .expect("Could not read archive");
+ let mut found_path = PathBuf::new();
+ for entry in entries {
+ let entry = entry
+ .expect("Could not read entry");
+ found_path = entry.path();
+ assert_file_contents(&found_path.join("cool-test-file-😎.gmi"), "# I try.");
+ assert!(&found_path.join("metadata.json").exists());
+ }
+
+ // Create Second Test File
+ let second_test_file = test_dir.join("cooler-test-file-😎.gmi");
+ let second_test_file_string = second_test_file.display().to_string();
+ create_test_file(&second_test_file, "# I REALLY try.");
+
+ // Update 2 of 2
+ update.execute(Some(&second_test_file_string), &configuration, "update")
+ .expect("Could not update a .gmi");
+ assert_file_contents(&posts_dir.join("0/cooler-test-file-😎.gmi"), "# I REALLY try.");
+ assert_file_contents(&found_path.join("cooler-test-file-😎.gmi"), "# I REALLY try.");
+
+ cleanup_test_dir(&test_dir);
+ }
+
+ #[test]
+ fn test_write_metadata_fail() {
+ // Create all directories
+ let test_dir = setup_test_dir();
+
+ // Reference but don't create the directories
+ let metadata_dir = test_dir.join("metadata/nested/cannot/write");
+
+ // Create metadata
+ let metadata = Metadata {
+ id: "1234".to_string(),
+ created_on: 1234
+ };
+
+ // Update 1 of 2
+ let result = Update::write_metadata(&metadata, &metadata_dir);
+ assert!(result.is_err());
+
+ cleanup_test_dir(&test_dir);
+ }
+
+ #[test]
+ fn test_recursive_archive() {
+ // Create all directories
+ let test_dir = setup_test_dir();
+
+ // Create the directories
+ let source_dir = test_dir.join("source");
+ let target_dir = test_dir.join("target");
+ let nested_dir = source_dir.join("nested");
+ create_dir_all(&nested_dir).expect("Could not create source dir.");
+ create_dir_all(&target_dir).expect("Could not create target dir.");
+
+ // Create the two test files.
+ let test_file = source_dir.join("not_nested.gmi");
+ let nested_test_file = nested_dir.join("very_much_nested.gmi");
+ create_test_file(&test_file, "# Unlike a Bird");
+ create_test_file(&nested_test_file, "# Very much like a Bird");
+
+ Update::archive(&source_dir, &target_dir)
+ .expect("Could not archive the test directories");
+
+ assert_file_contents(&target_dir.join("not_nested.gmi"), "# Unlike a Bird");
+ assert_file_contents(&target_dir.join("nested/very_much_nested.gmi"), "# Very much like a Bird");
+
+ cleanup_test_dir(&test_dir);
+ }
+
+ #[test]
+ fn test_update_fails_on_directory() {
+ let update = Update::new();
+
+ // Create all directories
+ let test_dir = setup_test_dir();
+
+ // Reference but don't create the directories
+ let nested_dir = test_dir.join("nested");
+ let posts_dir = test_dir.join("posts");
+ let archive_dir = test_dir.join("archive");
+ create_dir_all(&nested_dir).expect("Could not create nested dir.");
+
+ // Create configuration
+ let mut configuration = Configuration::new();
+ configuration.posts_directory = posts_dir.clone();
+ configuration.archive_directory = archive_dir.clone();
+
+ // Create Test File
+ let test_file = nested_dir.join("cool-test-file-😎.gmi");
+ create_test_file(&test_file, "# I try.");
+ let nested_string = nested_dir.display().to_string();
+
+ let result = update.execute(Some(&nested_string), &configuration, "update");
+ assert!(result.is_err());
+ cleanup_test_dir(&test_dir);
+ }
+
+ #[test]
+ fn test_update_fails_on_missing_input() {
+ let update = Update::new();
+
+ // Create all directories
+ let test_dir = setup_test_dir();
+
+ // Reference but don't create the directories
+ let posts_dir = test_dir.join("posts");
+ let archive_dir = test_dir.join("archive");
+
+ // Create configuration
+ let mut configuration = Configuration::new();
+ configuration.posts_directory = posts_dir.clone();
+ configuration.archive_directory = archive_dir.clone();
+
+ // Add 1 of 3
+ let result = update.execute(None, &configuration, "update");
+ assert!(result.is_err());
+ cleanup_test_dir(&test_dir);
+ }
+
+ #[test]
+ fn test_update_fails_if_file_does_not_exist() {
+ let update = Update::new();
+
+ // Create all directories
+ let test_dir = setup_test_dir();
+
+ // Reference but don't create the directories
+ let posts_dir = test_dir.join("posts");
+ let archive_dir = test_dir.join("archive");
+
+ // Create configuration
+ let mut configuration = Configuration::new();
+ configuration.posts_directory = posts_dir.clone();
+ configuration.archive_directory = archive_dir.clone();
+
+ // Create Test File
+ let test_file = test_dir.join("cool-secret-test-file-😎.gmi");
+ let test_file_string = test_file.display().to_string();
+
+ // Add 1 of 3
+ let result = update.execute(Some(&test_file_string), &configuration, "update");
+ assert!(result.is_err());
+
+ cleanup_test_dir(&test_dir);
+ }
+
+ #[test]
+ fn update_before_dependencies() {
+ let update = Update::new();
+ let dependencies = update.before_dependencies();
+
+ assert_eq!(dependencies.len(), 1);
+ assert_eq!(dependencies[0].command(), "sync-down");
+ }
+
+ #[test]
+ fn update_after_dependencies() {
+ let update = Update::new();
+ let dependencies = update.after_dependencies();
+
+ assert_eq!(dependencies.len(), 2);
+ assert_eq!(dependencies[0].command(), "generate");
+ assert_eq!(dependencies[1].command(), "sync-up");
+ }
+
+ // These two tests feel pointless but I'm doing it for the coverage :p
+
+ #[test]
+ fn update_command_output() {
+ let update = Update::new();
+ update.command();
+ }
+
+ #[test]
+ fn update_help_output() {
+ let update = Update::new();
+ update.help();
+ }
+}
diff --git a/src/remote/git.rs b/src/remote/git.rs
index 75e6292..912d8c1 100644
--- a/src/remote/git.rs
+++ b/src/remote/git.rs
@@ -13,10 +13,8 @@ impl Git {
impl super::Remote for Git {
fn can_handle(&self, remote: &str) -> bool {
- let command = format!("git ls-remote {remote}");
- match Command::new("sh")
- .arg("-c")
- .arg(&command)
+ match Command::new("git")
+ .args(["ls-remote", remote])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
@@ -32,25 +30,18 @@ impl super::Remote for Git {
.map_err(|_| Error::new(Other, "Invalid time"))?
.as_millis();
- let commands = vec![
- format!("cd {} && git init -b main", directory.display()),
- format!("cd {} && git add .", directory.display()),
- format!(
- "cd {} && git commit --allow-empty -m blog-sync-up-{}",
- directory.display(),
- timestamp
- ),
- format!(
- "cd {} && git push {} main --force",
- directory.display(),
- remote
- ),
+ let commit_name = format!("blog-sync-up-{}", timestamp);
+ let command_arguments = vec![
+ vec!["init", "-b", "main"],
+ vec!["add", "."],
+ vec!["commit", "--allow-empty", "-m", &commit_name],
+ vec!["push", &remote, "main", "--force"],
];
- for command in commands {
- Command::new("sh")
- .arg("-c")
- .arg(&command)
+ for arguments in command_arguments {
+ Command::new("git")
+ .current_dir(&directory)
+ .args(&arguments)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
@@ -61,17 +52,17 @@ impl super::Remote for Git {
}
fn sync_down(&self, remote: &str, directory: &Path) -> Result<()> {
- let commands = vec![
- format!("cd {} && git init -b main", directory.display()),
- format!("cd {} && git checkout .", directory.display()),
- format!("cd {} && git clean . -f", directory.display()),
- format!("cd {} && git pull {} main", directory.display(), remote),
+ let command_arguments = vec![
+ vec!["init", "-b", "main"],
+ vec!["checkout", "."],
+ vec!["clean", ".", "-f"],
+ vec!["pull", &remote, "main"],
];
- for command in commands {
- Command::new("sh")
- .arg("-c")
- .arg(&command)
+ for arguments in command_arguments {
+ Command::new("git")
+ .current_dir(&directory)
+ .args(&arguments)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
@@ -84,71 +75,12 @@ impl super::Remote for Git {
#[cfg(test)]
mod tests {
use std::fs::create_dir_all;
- use std::path::Path;
- use std::process::{Command, Stdio};
use super::*;
use crate::remote::Remote;
use test_utilities::*;
- fn create_remote(path: &Path) {
- let remote_path = path.display();
- Command::new("sh")
- .arg("-c")
- .arg(format!("cd {remote_path} && git init --bare"))
- .stdout(Stdio::null())
- .stderr(Stdio::null())
- .status()
- .expect("Failed to initialize bare repository");
- }
-
- fn assert_file_does_not_exist(repo_path: &str, file_path: &str) {
- let output = Command::new("sh")
- .arg("-c")
- .arg(format!("cd {repo_path} && git show main:{file_path}"))
- .stderr(Stdio::null())
- .output()
- .expect("Failed to read from repo");
-
- assert!(output.status.code().unwrap() != 0);
- }
-
- fn assert_file_in_repo_with_contents(repo_path: &str, file_path: &str, contents: &str) {
- let output = Command::new("sh")
- .arg("-c")
- .arg(format!("cd {repo_path} && git show main:{file_path}"))
- .stderr(Stdio::null())
- .output()
- .expect("Failed to read from repo");
-
- assert!(String::from_utf8(output.stdout)
- .map(|file| file.trim() == contents)
- .unwrap_or(false));
- }
-
- fn commit_file_to_remote(repo_path: &str, file_path: &str, contents: &str) {
- let test_dir = setup_test_dir();
- let test_dir_display = test_dir.display();
-
- Command::new("sh")
- .arg("-c")
- .arg(format!(
- "cd {test_dir_display} && \
- git clone {repo_path} . && \
- echo '{contents}' > {file_path} && \
- git add {file_path} && \
- git -c user.name='test' -c user.email='test@example.com' commit -m 'test commit' && \
- git push origin main",
- ))
- .stdout(Stdio::null())
- .stderr(Stdio::null())
- .status()
- .expect("Failed to commit file");
-
- cleanup_test_dir(&test_dir);
- }
-
#[test]
fn test_can_handle_git_remotes() {
let git = Git::new();
@@ -201,7 +133,7 @@ mod tests {
git.sync_up(&remote_dir_as_string, &local_dir)
.expect("Could not sync up to remote");
assert_file_in_repo_with_contents(&remote_dir_as_string, "file1.txt", "I exist");
- assert_file_does_not_exist(&remote_dir_as_string, "file2.txt");
+ assert_file_in_repo_does_not_exist(&remote_dir_as_string, "file2.txt");
create_test_file(&local_dir.join("file2.txt"), "I also exist now, btw");
git.sync_up(&remote_dir_as_string, &local_dir)
@@ -219,8 +151,8 @@ mod tests {
fn test_remote_up_force_pushes() {
let git = Git::new();
let test_dir = setup_test_dir();
- let local_dir = test_dir.join("local");
- let remote_dir = test_dir.join("remote");
+ let local_dir = test_dir.join("gitlocal");
+ let remote_dir = test_dir.join("gitremote");
create_dir_all(&local_dir).expect("Could not create local test directory");
create_dir_all(&remote_dir).expect("Could not create remote test directory");
@@ -229,7 +161,7 @@ mod tests {
let remote_dir_as_string = remote_dir.display().to_string();
commit_file_to_remote(&remote_dir_as_string, "boo.txt", "A ghost");
- assert_file_does_not_exist(&remote_dir_as_string, "file1.txt");
+ assert_file_in_repo_does_not_exist(&remote_dir_as_string, "file1.txt");
assert_file_in_repo_with_contents(&remote_dir_as_string, "boo.txt", "A ghost");
create_test_file(&local_dir.join("file1.txt"), "I exist");
@@ -237,7 +169,7 @@ mod tests {
.expect("Could not sync up to remote");
assert_file_in_repo_with_contents(&remote_dir_as_string, "file1.txt", "I exist");
- assert_file_does_not_exist(&remote_dir_as_string, "boo.txt");
+ assert_file_in_repo_does_not_exist(&remote_dir_as_string, "boo.txt");
cleanup_test_dir(&test_dir);
}
diff --git a/src/remote/mod.rs b/src/remote/mod.rs
index 9782b0d..1a2421d 100644
--- a/src/remote/mod.rs
+++ b/src/remote/mod.rs
@@ -70,6 +70,7 @@ fn read_remote(file_path: &Path) -> Option<String> {
#[cfg(test)]
mod tests {
+ use std::fs::create_dir_all;
use super::*;
@@ -102,4 +103,67 @@ mod tests {
cleanup_test_dir(&test_dir);
}
+
+ #[test]
+ fn test_syncs_remote_up() {
+ let test_dir = setup_test_dir();
+ let local_dir = test_dir.join("modlocal");
+ let remote_dir = test_dir.join("modremote");
+ create_dir_all(&local_dir).expect("Could not create local test directory");
+ create_dir_all(&remote_dir).expect("Could not create remote test directory");
+
+ create_remote(&remote_dir);
+
+ let remote_dir_as_string = remote_dir.display().to_string();
+ create_test_file(&test_dir.join("remoteconfig"), &remote_dir_as_string);
+
+ create_test_file(&local_dir.join("file1.txt"), "I exist");
+ sync_up(&local_dir, &test_dir.join("remoteconfig")).expect("Could not sync up to remote");
+ assert_file_in_repo_with_contents(&remote_dir_as_string, "file1.txt", "I exist");
+ assert_file_in_repo_does_not_exist(&remote_dir_as_string, "file2.txt");
+
+ create_test_file(&local_dir.join("file2.txt"), "I also exist now, btw");
+ sync_up(&local_dir, &test_dir.join("remoteconfig")).expect("Could not sync up to remote");
+ assert_file_in_repo_with_contents(&remote_dir_as_string, "file1.txt", "I exist");
+ assert_file_in_repo_with_contents(
+ &remote_dir_as_string,
+ "file2.txt",
+ "I also exist now, btw",
+ );
+ cleanup_test_dir(&test_dir);
+ }
+
+ #[test]
+ fn test_syncs_remote_down() {
+ let test_dir = setup_test_dir();
+ let local_dir = test_dir.join("modownlocal");
+ let remote_dir = test_dir.join("modownremote");
+ create_dir_all(&local_dir).expect("Could not create local test directory");
+ create_dir_all(&remote_dir).expect("Could not create remote test directory");
+
+ create_remote(&remote_dir);
+
+ let remote_dir_as_string = remote_dir.display().to_string();
+ create_test_file(&test_dir.join("remoteconfig"), &remote_dir_as_string);
+
+ commit_file_to_remote(&remote_dir_as_string, "file 1.txt", "I exist, but remotely");
+ assert!(!&local_dir.join("file 1.txt").exists());
+ assert!(!&local_dir.join("file2.txt").exists());
+ sync_down(&local_dir, &test_dir.join("remoteconfig"))
+ .expect("Could not sync down from remote");
+ assert_file_contents(&local_dir.join("file 1.txt"), "I exist, but remotely");
+ assert!(!&local_dir.join("file2.txt").exists());
+
+ commit_file_to_remote(
+ &remote_dir_as_string,
+ "file2.txt",
+ "I also exist, but remotely",
+ );
+ assert!(!&local_dir.join("file2.txt").exists());
+ sync_down(&local_dir, &test_dir.join("remoteconfig"))
+ .expect("Could not sync down from remote");
+ assert_file_contents(&local_dir.join("file 1.txt"), "I exist, but remotely");
+ assert_file_contents(&local_dir.join("file2.txt"), "I also exist, but remotely");
+ cleanup_test_dir(&test_dir);
+ }
}