]> git.r.bdr.sh - rbdr/blog/commitdiff
Add tests for utils and metadata
authorRuben Beltran del Rio <redacted>
Sun, 5 Jan 2025 19:52:26 +0000 (20:52 +0100)
committerRuben Beltran del Rio <redacted>
Sun, 5 Jan 2025 19:52:26 +0000 (20:52 +0100)
Cargo.lock
Cargo.toml
src/metadata.rs
src/utils.rs
test_utilities/Cargo.toml [new file with mode: 0644]
test_utilities/src/lib.rs [new file with mode: 0644]

index d9971b07f1154a52088811e8c3b4eb5cf7804a8f..56a10d0aa8e6358f4328a2949271c237cf7bdd5d 100644 (file)
@@ -9,6 +9,7 @@ dependencies = [
  "gema_texto",
  "serde",
  "serde_json",
+ "test_utilities",
  "time",
 ]
 
@@ -118,6 +119,10 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "test_utilities"
+version = "7.1.0"
+
 [[package]]
 name = "time"
 version = "0.3.37"
index 5a6482468c303da3f8ac849b7c5c7410bde15514..cfea8d4312aa77097c63a33aa6b4083827a6998f 100644 (file)
@@ -13,6 +13,9 @@ time = { version = "0.3.37", features = ["macros", "formatting"] }
 serde = { version = "1.0.217", features = ["derive"] }
 serde_json = "1.0.134"
 
+[dev-dependencies]
+test_utilities = { path = "test_utilities" }
+
 [profile.release]
 strip = true
 lto = true
index fe657e4e90bceaf45374bd3c215b08c79dcfc7ab..d6b3d6aa9fd3642e155fab5c96b93bb542800d6d 100644 (file)
@@ -13,6 +13,11 @@ pub struct Metadata {
 }
 
 impl Metadata {
+    /// Reads `Metadata` from a file or creates a new one.
+    ///
+    /// This method will create new metadata if it can't read the file,
+    /// whether it's because of malformed JSON, UTF-8 or because the file
+    /// is not readable.
     pub fn read_or_create(file_path: &PathBuf) -> Metadata {
         if let Some(metadata) = Metadata::read_metadata_file(file_path) {
             metadata
@@ -28,6 +33,7 @@ impl Metadata {
         }
     }
 
+    /// Returns the metadata `created_on` as an RFC-2822 string.
     pub fn created_on_utc(&self) -> Option<String> {
         let date =
             OffsetDateTime::from_unix_timestamp_nanos((self.created_on * 1_000_000).into()).ok()?;
@@ -41,3 +47,62 @@ impl Metadata {
         serde_json::from_str(&contents).ok()
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use std::fs::write;
+
+    use super::*;
+
+    use test_utilities::*;
+
+    #[test]
+    fn test_reads_metadata_if_file_exists() {
+        let test_dir = setup_test_dir();
+        create_test_file(
+            &test_dir.join("metadata.json"),
+            "\
+{
+    \"id\": \"cool\",
+    \"created_on\": 1736105008957
+}",
+        );
+
+        let metadata = Metadata::read_or_create(&test_dir.join("metadata.json"));
+
+        assert_eq!(metadata.id, "cool");
+        assert_eq!(metadata.created_on, 1736105008957);
+    }
+
+    #[test]
+    fn test_creates_metadata_if_file_does_not_exist() {
+        let test_dir = setup_test_dir();
+
+        assert!(!test_dir.join("metadata.json").exists());
+        let metadata = Metadata::read_or_create(&test_dir.join("metadata.json"));
+        assert_eq!(metadata.created_on.to_string(), metadata.id);
+    }
+
+    #[test]
+    fn test_creates_metadata_if_file_is_malformed() {
+        let test_dir = setup_test_dir();
+        write(&test_dir.join("metadata.json"), vec![0xFF, 0xFF]).expect("Failed to write file");
+
+        let metadata = Metadata::read_or_create(&test_dir.join("metadata.json"));
+        assert_eq!(metadata.created_on.to_string(), metadata.id);
+    }
+
+    #[test]
+    fn test_it_returns_created_on_as_rfc_2822_utc() {
+        let metadata = Metadata {
+            id: "cool".to_string(),
+            created_on: 1736035200000,
+        };
+
+        if let Some(created_on_utc) = metadata.created_on_utc() {
+            assert_eq!(created_on_utc, "Sun, 05 Jan 2025 00:00:00 +0000");
+        } else {
+            panic!("Could not generate RFC-2822 string");
+        }
+    }
+}
index 4522f4cc94b0efeae6171f5284859af488b2e4c4..01b6cdf2360a72b13964315c0b8599f8ebf49278 100644 (file)
@@ -2,6 +2,7 @@ use std::fs::{copy, create_dir_all, read_dir};
 use std::io::Result;
 use std::path::Path;
 
+/// Recursively copies the contents of a directory.
 pub fn recursively_copy(source: &Path, target: &Path) -> Result<()> {
     let entries = read_dir(source)?;
     for entry in entries {
@@ -21,3 +22,50 @@ pub fn recursively_copy(source: &Path, target: &Path) -> Result<()> {
 
     Ok(())
 }
+
+#[cfg(test)]
+mod tests {
+    use std::fs::create_dir_all;
+
+    use super::*;
+
+    use test_utilities::*;
+
+    #[test]
+    fn test_recursively_copies_directories() {
+        let test_dir = setup_test_dir();
+        let source_dir = test_dir.join("source");
+        let target_dir = test_dir.join("target");
+        let nested_dir = source_dir.join("nested");
+        let hidden_dir = source_dir.join(".hidden");
+        create_dir_all(&target_dir).expect("Could not create target test directory");
+        create_dir_all(&nested_dir).expect("Could not create nested test directory");
+        create_dir_all(&hidden_dir).expect("Could not create hidden test directory");
+        create_test_file(&source_dir.join("image.png"), "A beautiful pencil");
+        create_test_file(&nested_dir.join("hello.txt"), "I am here");
+        create_test_file(&nested_dir.join(".hidden"), "I am not here?");
+        create_test_file(&hidden_dir.join("in_plain_sight.mkv"), "Almost");
+        create_test_file(&hidden_dir.join(".hidden"), "I'm double hidden");
+
+        recursively_copy(&source_dir, &target_dir).expect("Copy failed on test directory");
+
+        assert_file_contents(&target_dir.join("image.png"), "A beautiful pencil");
+        assert_file_contents(&target_dir.join("nested/hello.txt"), "I am here");
+        assert_file_contents(&target_dir.join("nested/.hidden"), "I am not here?");
+        assert_file_contents(&target_dir.join(".hidden/in_plain_sight.mkv"), "Almost");
+        assert_file_contents(&target_dir.join(".hidden/.hidden"), "I'm double hidden");
+    }
+
+    #[test]
+    fn test_empty_directory() {
+        let test_dir = setup_test_dir();
+        let source_dir = test_dir.join("source");
+        let target_dir = test_dir.join("target");
+        create_dir_all(&source_dir).expect("Could not create source test directory");
+        create_dir_all(&target_dir).expect("Could not create target test directory");
+
+        recursively_copy(&source_dir, &target_dir).expect("Copy failed on empty test directory");
+
+        assert_eq!(target_dir.read_dir().unwrap().count(), 0);
+    }
+}
diff --git a/test_utilities/Cargo.toml b/test_utilities/Cargo.toml
new file mode 100644 (file)
index 0000000..57ed81a
--- /dev/null
@@ -0,0 +1,10 @@
+[package]
+name = "test_utilities"
+version = "7.1.0"
+edition = "2021"
+license = "AGPL-3.0-or-later"
+description = "Shared test utilities for blog"
+homepage = "https://r.bdr.sh/page.html"
+authors = ["Rubén Beltrán del Río <page@r.bdr.sh>"]
+
+[dependencies]
diff --git a/test_utilities/src/lib.rs b/test_utilities/src/lib.rs
new file mode 100644 (file)
index 0000000..f5a7b39
--- /dev/null
@@ -0,0 +1,34 @@
+use std::env::temp_dir;
+use std::fs::{create_dir_all, read_to_string, remove_dir_all, File};
+use std::io::Write;
+use std::path::PathBuf;
+use std::time::{SystemTime, UNIX_EPOCH};
+use std::process::id;
+
+pub fn setup_test_dir() -> PathBuf {
+    let timestamp = SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .unwrap()
+        .as_nanos();
+    let process_id = id();
+    let test_dir = temp_dir().join(format!("blog_test_{}_{}", timestamp, process_id));
+    create_dir_all(&test_dir)
+        .expect("Could not create test directory");
+    test_dir
+}
+
+pub fn cleanup_test_dir(test_dir: &PathBuf) {
+    if test_dir.exists() {
+        remove_dir_all(test_dir).unwrap();
+    }
+}
+
+pub fn create_test_file(path: &PathBuf, content: &str) {
+    let mut file = File::create(path).unwrap();
+    file.write_all(content.as_bytes()).unwrap();
+}
+
+pub fn assert_file_contents(path: &PathBuf, expected: &str) {
+    let content = read_to_string(path).unwrap();
+    assert_eq!(content.trim(), expected.trim());
+}