use bim::collections::Collection;
use bim::config::Config;
use bim::content::{Page, Site};
use bim::discovery::discover_site;
use bim::render::build_site;
use std::fs;
use std::path::PathBuf;
use tempfile::TempDir;
fn create_test_site() -> TempDir {
let dir = TempDir::new().unwrap();
let root = dir.path();
fs::create_dir_all(root.join("blog")).unwrap();
fs::write(
root.join("blog/2024-01-15-hello-world.md"),
"# Hello World\n\nThis is my first blog post.\n\n[#rust] [#programming]",
)
.unwrap();
fs::create_dir_all(root.join("notes/rust")).unwrap();
fs::write(
root.join("notes/rust/ownership.md"),
"# Rust Ownership\n\nOwnership is Rust's key feature.\n\nSee also: [hello world](/blog/hello-world)",
)
.unwrap();
fs::write(
root.join("index.md"),
"# My Site\n\nWelcome to my site.\n\n{{recent blog 3}}",
)
.unwrap();
fs::write(root.join("nav.md"), "- [Blog](/blog)\n- [Notes](/notes)").unwrap();
fs::create_dir_all(root.join("layouts")).unwrap();
fs::write(
root.join("layouts/default.html"),
r#"<!DOCTYPE html>
<html lang="{{lang}}" dir="{{dir}}">
<head>
<meta charset="utf-8">
<title>{{title}}</title>
</head>
<body>
<nav>{{nav}}</nav>
<article>{{content}}</article>
</body>
</html>"#,
)
.unwrap();
fs::create_dir_all(root.join("static/css")).unwrap();
fs::write(root.join("static/css/style.css"), "body { margin: 0; }").unwrap();
fs::create_dir_all(root.join("tags")).unwrap();
dir
}
#[test]
fn test_full_site_build() {
let source = create_test_site();
let output = TempDir::new().unwrap();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: output.path().to_path_buf(),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
build_site(&site, &config).expect("Site build failed");
assert!(output.path().join("index.html").exists());
assert!(output.path().join("blog/hello-world/index.html").exists());
assert!(output.path().join("notes/rust/ownership/index.html").exists());
assert!(output.path().join("feed.xml").exists());
assert!(output.path().join("sitemap.xml").exists());
assert!(output.path().join("css/style.css").exists());
}
#[test]
fn test_blog_collection_discovery() {
let source = create_test_site();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: PathBuf::from("_test"),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
let blog_posts: Vec<&Page> = site.pages_by_collection(Collection::Blog);
assert!(!blog_posts.is_empty(), "Should have blog posts");
let hello_post = blog_posts.iter().find(|p| p.url.contains("hello-world"));
assert!(hello_post.is_some(), "Should find hello-world post");
let post = hello_post.unwrap();
assert_eq!(post.title, "Hello World");
assert!(post.date.is_some(), "Blog post should have a date");
}
#[test]
fn test_notes_collection_discovery() {
let source = create_test_site();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: PathBuf::from("_test"),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
let notes: Vec<&Page> = site.pages_by_collection(Collection::Notes);
assert!(!notes.is_empty(), "Should have notes");
let ownership_note = notes.iter().find(|p| p.url.contains("ownership"));
assert!(ownership_note.is_some(), "Should find ownership note");
}
#[test]
fn test_tag_extraction() {
let source = create_test_site();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: PathBuf::from("_test"),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
let blog_posts: Vec<&Page> = site.pages_by_collection(Collection::Blog);
let hello_post = blog_posts.iter().find(|p| p.url.contains("hello-world")).unwrap();
assert!(!hello_post.tags.is_empty(), "Post should have tags");
assert!(
hello_post.tags.iter().any(|t| t.name == "rust"),
"Post should have #rust tag"
);
}
#[test]
fn test_internal_link_extraction() {
let source = create_test_site();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: PathBuf::from("_test"),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
let notes: Vec<&Page> = site.pages_by_collection(Collection::Notes);
let ownership_note = notes.iter().find(|p| p.url.contains("ownership")).unwrap();
assert!(!ownership_note.links.is_empty(), "Note should have internal links");
}
#[test]
fn test_navigation_loading() {
let source = create_test_site();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: PathBuf::from("_test"),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
assert!(!site.nav_html.is_empty(), "Should have navigation HTML");
assert!(site.nav_html.contains("Blog"), "Nav should contain Blog link");
assert!(site.nav_html.contains("Notes"), "Nav should contain Notes link");
}
#[test]
fn test_draft_exclusion() {
let source = create_test_site();
fs::write(
source.path().join("blog/_draft-post.md"),
"# Draft Post\n\nThis is a draft.",
)
.unwrap();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: PathBuf::from("_test"),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
let draft = site.pages.iter().find(|p| p.url.contains("draft-post"));
assert!(draft.is_none(), "Draft should be excluded when include_drafts is false");
}
#[test]
fn test_draft_inclusion() {
let source = create_test_site();
fs::write(
source.path().join("blog/_draft-post.md"),
"# Draft Post\n\nThis is a draft.",
)
.unwrap();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: PathBuf::from("_test"),
include_drafts: true,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
let draft = site.pages.iter().find(|p| p.url.contains("draft-post"));
assert!(draft.is_some(), "Draft should be included when include_drafts is true");
}
#[test]
fn test_static_file_copying() {
let source = create_test_site();
let output = TempDir::new().unwrap();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: output.path().to_path_buf(),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
build_site(&site, &config).expect("Site build failed");
assert!(output.path().join("css/style.css").exists());
let css_content = fs::read_to_string(output.path().join("css/style.css")).unwrap();
assert!(css_content.contains("margin: 0"));
}
#[test]
fn test_rss_feed_generation() {
let source = create_test_site();
let output = TempDir::new().unwrap();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: output.path().to_path_buf(),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
build_site(&site, &config).expect("Site build failed");
let rss = fs::read_to_string(output.path().join("feed.xml")).unwrap();
assert!(rss.contains("<rss"));
assert!(rss.contains("<channel>"));
assert!(rss.contains("Hello World"));
}
#[test]
fn test_sitemap_generation() {
let source = create_test_site();
let output = TempDir::new().unwrap();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: output.path().to_path_buf(),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
build_site(&site, &config).expect("Site build failed");
let sitemap = fs::read_to_string(output.path().join("sitemap.xml")).unwrap();
assert!(sitemap.contains("<urlset"));
assert!(sitemap.contains("<url>"));
assert!(sitemap.contains("<loc>"));
}
#[test]
fn test_html_output_structure() {
let source = create_test_site();
let output = TempDir::new().unwrap();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: output.path().to_path_buf(),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
build_site(&site, &config).expect("Site build failed");
let index_html = fs::read_to_string(output.path().join("index.html")).unwrap();
assert!(index_html.contains("<!DOCTYPE html>"));
assert!(index_html.contains("<html"));
assert!(index_html.contains("<head>"));
assert!(index_html.contains("<body>"));
assert!(index_html.contains("<nav>"));
assert!(index_html.contains("<article>"));
}
#[test]
fn test_site_methods() {
let site = Site::new();
assert!(site.pages.is_empty());
assert!(site.snippets.is_empty());
let source = create_test_site();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: PathBuf::from("_test"),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
let blog_pages = site.pages_by_collection(Collection::Blog);
assert!(!blog_pages.is_empty());
let recent = site.recent_pages(Collection::Blog, 1);
assert_eq!(recent.len(), 1);
let root = site.find_page_by_url("/");
assert!(root.is_some());
}
#[test]
fn test_page_date_extraction_from_filename() {
let source = create_test_site();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: PathBuf::from("_test"),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
let blog_posts: Vec<&Page> = site.pages_by_collection(Collection::Blog);
let hello_post = blog_posts.iter().find(|p| p.url.contains("hello-world")).unwrap();
assert!(hello_post.date.is_some());
let date = hello_post.date.unwrap();
assert_eq!(date.format("%Y-%m-%d").to_string(), "2024-01-15");
}
#[test]
fn test_url_generation() {
let source = create_test_site();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: PathBuf::from("_test"),
include_drafts: false,
..Default::default()
};
let site = discover_site(&config).expect("Site discovery failed");
let root = site.find_page_by_url("/");
assert!(root.is_some());
let blog_posts: Vec<&Page> = site.pages_by_collection(Collection::Blog);
let hello_post = blog_posts.iter().find(|p| p.url.contains("hello-world")).unwrap();
assert!(!hello_post.url.contains("2024-01-15"));
assert!(hello_post.url.starts_with("/blog/"));
}
#[test]
fn test_clean_build() {
let source = create_test_site();
let output = TempDir::new().unwrap();
fs::create_dir_all(output.path()).unwrap();
fs::write(output.path().join("stale.txt"), "stale content").unwrap();
let config = Config {
source_dir: source.path().to_path_buf(),
output_dir: output.path().to_path_buf(),
include_drafts: false,
..Default::default()
};
if config.output_dir.exists() {
fs::remove_dir_all(&config.output_dir).unwrap();
}
let site = discover_site(&config).expect("Site discovery failed");
build_site(&site, &config).expect("Site build failed");
assert!(!output.path().join("stale.txt").exists());
assert!(output.path().join("index.html").exists());
}