Skip to content

Commit 324ff21

Browse files
test(ssg): ✅ add unit tests
1 parent 547c62e commit 324ff21

File tree

1 file changed

+369
-1
lines changed

1 file changed

+369
-1
lines changed

src/process.rs

Lines changed: 369 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,19 @@ pub fn ensure_directory(
113113
path: &Path,
114114
dir_type: &str,
115115
) -> Result<(), ProcessError> {
116-
if !path.exists() {
116+
if path.exists() {
117+
// Check if the existing path is a directory
118+
if !path.is_dir() {
119+
return Err(ProcessError::DirectoryCreation {
120+
dir_type: dir_type.to_string(),
121+
path: path.display().to_string(),
122+
source: std::io::Error::new(
123+
std::io::ErrorKind::AlreadyExists,
124+
"Path exists but is not a directory",
125+
),
126+
});
127+
}
128+
} else {
117129
fs::create_dir_all(path).map_err(|e| {
118130
ProcessError::DirectoryCreation {
119131
dir_type: dir_type.to_string(),
@@ -262,6 +274,8 @@ pub fn args(matches: &ArgMatches) -> Result<(), ProcessError> {
262274
mod tests {
263275
use super::*;
264276
use clap::{arg, Command};
277+
use std::fs::Permissions;
278+
use std::fs::{self, File};
265279
use tempfile::tempdir;
266280

267281
/// Helper function to create a test `ArgMatches` with all required arguments.
@@ -460,4 +474,358 @@ mod tests {
460474

461475
Ok(())
462476
}
477+
#[test]
478+
fn test_process_frontmatter_with_valid_frontmatter(
479+
) -> Result<(), ProcessError> {
480+
let content = "\
481+
---
482+
title: Test Post
483+
date: 2024-01-01
484+
---
485+
# Main Content
486+
This is the main content.";
487+
488+
let processed = process_frontmatter(content)?;
489+
assert!(processed.contains("<!--frontmatter-processed-->"));
490+
assert!(processed.contains("title: Test Post"));
491+
assert!(processed.contains("# Main Content"));
492+
Ok(())
493+
}
494+
495+
#[test]
496+
fn test_process_frontmatter_without_frontmatter(
497+
) -> Result<(), ProcessError> {
498+
let content = "# Just Content\nNo frontmatter here.";
499+
let processed = process_frontmatter(content)?;
500+
assert_eq!(processed, content);
501+
Ok(())
502+
}
503+
504+
#[test]
505+
fn test_process_frontmatter_with_empty_frontmatter(
506+
) -> Result<(), ProcessError> {
507+
let content = "---\n---\nContent after empty frontmatter";
508+
let processed = process_frontmatter(content)?;
509+
assert!(processed.contains("<!--frontmatter-processed-->"));
510+
Ok(())
511+
}
512+
513+
#[test]
514+
fn test_preprocess_content_with_multiple_files(
515+
) -> Result<(), ProcessError> {
516+
let temp_dir = tempdir()?;
517+
518+
// Create multiple markdown files
519+
let file1_path = temp_dir.path().join("post1.md");
520+
let file2_path = temp_dir.path().join("post2.md");
521+
let non_md_path = temp_dir.path().join("other.txt");
522+
523+
fs::write(&file1_path, "---\ntitle: Post 1\n---\nContent 1")?;
524+
fs::write(&file2_path, "---\ntitle: Post 2\n---\nContent 2")?;
525+
fs::write(&non_md_path, "Not a markdown file")?;
526+
527+
preprocess_content(temp_dir.path())?;
528+
529+
// Verify markdown files were processed
530+
let content1 = fs::read_to_string(&file1_path)?;
531+
let content2 = fs::read_to_string(&file2_path)?;
532+
let other = fs::read_to_string(&non_md_path)?;
533+
534+
assert!(content1.contains("<!--frontmatter-processed-->"));
535+
assert!(content2.contains("<!--frontmatter-processed-->"));
536+
assert_eq!(other, "Not a markdown file");
537+
538+
Ok(())
539+
}
540+
541+
#[test]
542+
fn test_preprocess_content_with_non_existent_directory(
543+
) -> Result<(), ProcessError> {
544+
let non_existent = Path::new("non_existent_directory");
545+
let result = preprocess_content(non_existent);
546+
assert!(result.is_ok());
547+
Ok(())
548+
}
549+
550+
#[test]
551+
fn test_preprocess_content_with_invalid_permissions() {
552+
use std::os::unix::fs::PermissionsExt;
553+
554+
let temp_dir = tempdir().unwrap();
555+
let file_path = temp_dir.path().join("readonly.md");
556+
557+
// Create file with frontmatter
558+
fs::write(&file_path, "---\ntitle: Test\n---\nContent")
559+
.unwrap();
560+
561+
// Make file read-only
562+
fs::set_permissions(&file_path, Permissions::from_mode(0o444))
563+
.unwrap();
564+
565+
let result = preprocess_content(temp_dir.path());
566+
assert!(result.is_err());
567+
568+
// Reset permissions for cleanup
569+
fs::set_permissions(&file_path, Permissions::from_mode(0o666))
570+
.unwrap();
571+
}
572+
573+
#[test]
574+
fn test_internal_compile_error_handling() {
575+
let temp_dir = tempdir().unwrap();
576+
let result = internal_compile(
577+
&temp_dir.path().join("build"),
578+
&temp_dir.path().join("content"),
579+
&temp_dir.path().join("site"),
580+
&temp_dir.path().join("template"),
581+
);
582+
assert!(result.is_err());
583+
}
584+
585+
#[test]
586+
fn test_get_argument_with_empty_value() {
587+
let matches = Command::new("test")
588+
.arg(arg!(--"empty" <EMPTY> "Empty value"))
589+
.get_matches_from(vec!["test", "--empty", ""]);
590+
591+
let result = get_argument(&matches, "empty");
592+
assert!(result.is_ok());
593+
assert_eq!(result.unwrap(), "");
594+
}
595+
596+
#[test]
597+
fn test_ensure_directory_with_existing_file(
598+
) -> Result<(), Box<dyn std::error::Error>> {
599+
let temp_dir = tempdir()?;
600+
let file_path = temp_dir.path().join("existing_file");
601+
602+
// Create a file instead of a directory
603+
let _file = File::create(&file_path)?;
604+
605+
// Attempt to ensure directory at the same path
606+
let result = ensure_directory(&file_path, "test");
607+
608+
// Verify that the operation failed because path exists but is not a directory
609+
assert!(result.is_err());
610+
if let Err(ProcessError::DirectoryCreation { source, .. }) =
611+
result
612+
{
613+
assert_eq!(
614+
source.kind(),
615+
std::io::ErrorKind::AlreadyExists
616+
);
617+
} else {
618+
panic!("Expected DirectoryCreation error");
619+
}
620+
621+
Ok(())
622+
}
623+
624+
#[test]
625+
fn test_ensure_directory_with_existing_directory(
626+
) -> Result<(), Box<dyn std::error::Error>> {
627+
let temp_dir = tempdir()?;
628+
let dir_path = temp_dir.path().join("existing_dir");
629+
630+
// First create the directory
631+
fs::create_dir(&dir_path)?;
632+
633+
// Attempt to ensure directory at the same path
634+
let result = ensure_directory(&dir_path, "test");
635+
636+
// Should succeed because path exists and is a directory
637+
assert!(result.is_ok());
638+
639+
Ok(())
640+
}
641+
642+
#[test]
643+
fn test_preprocess_content_with_invalid_utf8() -> Result<()> {
644+
let temp_dir = tempdir()?;
645+
let file_path = temp_dir.path().join("invalid.md");
646+
647+
// Write invalid UTF-8 bytes
648+
let invalid_bytes = vec![0xFF, 0xFF];
649+
fs::write(&file_path, invalid_bytes)?;
650+
651+
let result = preprocess_content(temp_dir.path());
652+
assert!(result.is_err());
653+
Ok(())
654+
}
655+
656+
#[test]
657+
fn test_process_frontmatter_with_multiple_delimiters() -> Result<()>
658+
{
659+
let content = "\
660+
---
661+
title: First
662+
---
663+
---
664+
title: Second
665+
---
666+
Content";
667+
668+
let processed = process_frontmatter(content)?;
669+
// Should only process the first frontmatter section
670+
assert!(processed.contains("title: First"));
671+
assert!(processed.contains("---\ntitle: Second"));
672+
Ok(())
673+
}
674+
675+
#[test]
676+
fn test_process_frontmatter_with_malformed_delimiters(
677+
) -> Result<(), ProcessError> {
678+
// Test case where there's only one delimiter
679+
let content = "---\ntitle: Test\nContent";
680+
let processed = process_frontmatter(content)?;
681+
assert_eq!(processed, content); // Should remain unchanged with single delimiter
682+
683+
// Test case with extra spaces in delimiters (this should still be valid frontmatter)
684+
let content = "---\ntitle: Test\n---\nContent";
685+
let processed = process_frontmatter(content)?;
686+
assert!(processed.contains("<!--frontmatter-processed-->"));
687+
assert!(processed.contains("title: Test"));
688+
assert!(processed.contains("Content"));
689+
690+
Ok(())
691+
}
692+
693+
#[test]
694+
fn test_process_frontmatter_with_whitespace(
695+
) -> Result<(), ProcessError> {
696+
// Test with whitespace before first delimiter
697+
let content = "\n\n---\ntitle: Test\n---\nContent";
698+
let processed = process_frontmatter(content)?;
699+
// Should still process valid frontmatter even with leading whitespace
700+
assert!(processed.contains("<!--frontmatter-processed-->"));
701+
assert!(processed.contains("title: Test"));
702+
assert!(processed.contains("Content"));
703+
704+
// Test with mixed whitespace in frontmatter
705+
let content =
706+
"---\n title: Test \n author: Someone \n---\nContent";
707+
let processed = process_frontmatter(content)?;
708+
assert!(processed.contains("<!--frontmatter-processed-->"));
709+
assert!(processed.contains("title: Test"));
710+
assert!(processed.contains("author: Someone"));
711+
assert!(processed.contains("Content"));
712+
713+
Ok(())
714+
}
715+
716+
#[test]
717+
fn test_process_frontmatter_with_invalid_format(
718+
) -> Result<(), ProcessError> {
719+
// Missing second delimiter completely
720+
let content = "---\ntitle: Test\nContent";
721+
let processed = process_frontmatter(content)?;
722+
assert_eq!(processed, content);
723+
724+
// Wrong delimiter character
725+
let content = "===\ntitle: Test\n===\nContent";
726+
let processed = process_frontmatter(content)?;
727+
assert_eq!(processed, content);
728+
729+
// Empty content between delimiters
730+
let content = "---\n\n---\nContent";
731+
let processed = process_frontmatter(content)?;
732+
assert!(processed.contains("<!--frontmatter-processed-->"));
733+
734+
Ok(())
735+
}
736+
737+
#[test]
738+
fn test_preprocess_content_with_nested_directories(
739+
) -> Result<(), ProcessError> {
740+
let temp_dir = tempdir()?;
741+
let nested_dir = temp_dir.path().join("nested");
742+
fs::create_dir(&nested_dir)?;
743+
744+
// Create files in both root and nested directory
745+
let root_file = temp_dir.path().join("root.md");
746+
let nested_file = nested_dir.join("nested.md");
747+
748+
fs::write(&root_file, "---\ntitle: Root\n---\nRoot content")?;
749+
fs::write(
750+
&nested_file,
751+
"---\ntitle: Nested\n---\nNested content",
752+
)?;
753+
754+
preprocess_content(temp_dir.path())?;
755+
756+
// Verify only root file was processed (since we don't recurse into subdirectories)
757+
let root_content = fs::read_to_string(&root_file)?;
758+
assert!(root_content.contains("<!--frontmatter-processed-->"));
759+
760+
let nested_content = fs::read_to_string(&nested_file)?;
761+
assert!(
762+
!nested_content.contains("<!--frontmatter-processed-->")
763+
);
764+
765+
Ok(())
766+
}
767+
768+
#[test]
769+
fn test_preprocess_content_with_empty_files(
770+
) -> Result<(), ProcessError> {
771+
let temp_dir = tempdir()?;
772+
let empty_file = temp_dir.path().join("empty.md");
773+
774+
// Create empty markdown file
775+
fs::write(&empty_file, "")?;
776+
777+
preprocess_content(temp_dir.path())?;
778+
779+
// Verify empty file remains unchanged
780+
let content = fs::read_to_string(&empty_file)?;
781+
assert!(content.is_empty());
782+
783+
Ok(())
784+
}
785+
786+
#[test]
787+
fn test_ensure_directory_with_symlink() -> Result<(), ProcessError>
788+
{
789+
let temp_dir = tempdir()?;
790+
let real_dir = temp_dir.path().join("real_dir");
791+
let symlink = temp_dir.path().join("symlink_dir");
792+
793+
fs::create_dir(&real_dir)?;
794+
795+
#[cfg(unix)]
796+
std::os::unix::fs::symlink(&real_dir, &symlink)?;
797+
#[cfg(windows)]
798+
std::os::windows::fs::symlink_dir(&real_dir, &symlink)?;
799+
800+
// Should succeed as symlink points to a valid directory
801+
let result = ensure_directory(&symlink, "symlink");
802+
assert!(result.is_ok());
803+
804+
Ok(())
805+
}
806+
807+
#[test]
808+
fn test_internal_compile_with_empty_directories() {
809+
let temp_dir = tempdir().unwrap();
810+
811+
// Create empty required directories
812+
let build_dir = temp_dir.path().join("build");
813+
let content_dir = temp_dir.path().join("content");
814+
let site_dir = temp_dir.path().join("site");
815+
let template_dir = temp_dir.path().join("template");
816+
817+
fs::create_dir_all(&build_dir).unwrap();
818+
fs::create_dir_all(&content_dir).unwrap();
819+
fs::create_dir_all(&site_dir).unwrap();
820+
fs::create_dir_all(&template_dir).unwrap();
821+
822+
let result = internal_compile(
823+
&build_dir,
824+
&content_dir,
825+
&site_dir,
826+
&template_dir,
827+
);
828+
829+
assert!(result.is_err());
830+
}
463831
}

0 commit comments

Comments
 (0)