aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Beltran del Rio <git@r.bdr.sh>2025-01-05 20:52:26 +0100
committerRuben Beltran del Rio <git@r.bdr.sh>2025-01-05 20:52:26 +0100
commit0fca7c5476099212ed1fa9ed812aa456ba004300 (patch)
tree3acacb019691f56a510ddf21f929816e45d21d9a
parentb17907faf8d9693cef94a6048d802bd4ced9102f (diff)
Add tests for utils and metadata
-rw-r--r--Cargo.lock5
-rw-r--r--Cargo.toml3
-rw-r--r--src/metadata.rs65
-rw-r--r--src/utils.rs48
-rw-r--r--test_utilities/Cargo.toml10
-rw-r--r--test_utilities/src/lib.rs34
6 files changed, 165 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d9971b0..56a10d0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9,6 +9,7 @@ dependencies = [
"gema_texto",
"serde",
"serde_json",
+ "test_utilities",
"time",
]
@@ -119,6 +120,10 @@ dependencies = [
]
[[package]]
+name = "test_utilities"
+version = "7.1.0"
+
+[[package]]
name = "time"
version = "0.3.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 5a64824..cfea8d4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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
diff --git a/src/metadata.rs b/src/metadata.rs
index fe657e4..d6b3d6a 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -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");
+ }
+ }
+}
diff --git a/src/utils.rs b/src/utils.rs
index 4522f4c..01b6cdf 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -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
index 0000000..57ed81a
--- /dev/null
+++ b/test_utilities/Cargo.toml
@@ -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
index 0000000..f5a7b39
--- /dev/null
+++ b/test_utilities/src/lib.rs
@@ -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());
+}