use super::*;
#[test]
fn test_header_double_slash() {
let h = header(CommentStyle::DoubleSlash);
assert!(h.contains("// This file is auto-generated by alef"));
assert!(!h.contains("Issues & docs:"));
assert!(!h.contains("github.com/xberg-io/alef"));
assert!(!h.contains("sample_crate"));
}
#[test]
fn test_e2e_header_names_the_e2e_regenerate_command() {
let h = e2e_header(CommentStyle::DoubleSlash);
assert!(h.contains("// To regenerate: alef e2e generate"));
assert!(!h.contains("// To regenerate: alef generate\n"));
}
#[test]
fn test_header_for_config_omits_issues_url_when_unconfigured() {
let cfg: crate::core::config::NewAlefConfig = toml::from_str(
r#"
[workspace]
languages = ["python"]
[[crates]]
name = "demo"
sources = ["src/lib.rs"]
"#,
)
.unwrap();
let resolved = cfg.resolve().unwrap().remove(0);
let h = header_for_config(CommentStyle::DoubleSlash, &resolved);
assert!(!h.contains("Issues & docs:"));
assert!(!h.contains("github.com/xberg-io/alef"));
}
#[test]
fn test_header_for_config_uses_configured_metadata() {
let cfg: crate::core::config::NewAlefConfig = toml::from_str(
r#"
[workspace]
languages = ["python"]
[workspace.generated_header]
issues_url = "https://docs.example.invalid/alef"
regenerate_command = "task generate"
verify_command = "task verify"
[[crates]]
name = "demo"
sources = ["src/lib.rs"]
"#,
)
.unwrap();
let resolved = cfg.resolve().unwrap().remove(0);
let h = header_for_config(CommentStyle::DoubleSlash, &resolved);
assert!(h.contains("// To regenerate: task generate"));
assert!(h.contains("// To verify freshness: task verify"));
assert!(h.contains("// Issues & docs: https://docs.example.invalid/alef"));
}
#[test]
fn test_header_for_config_uses_package_metadata_url() {
let cfg: crate::core::config::NewAlefConfig = toml::from_str(
r#"
[workspace]
languages = ["python"]
[[crates]]
name = "demo"
sources = ["src/lib.rs"]
[crates.package_metadata]
issues = "https://issues.example.invalid/demo"
"#,
)
.unwrap();
let resolved = cfg.resolve().unwrap().remove(0);
let h = header_for_config(CommentStyle::DoubleSlash, &resolved);
assert!(h.contains("// Issues & docs: https://issues.example.invalid/demo"));
}
#[test]
fn test_header_hash() {
let h = header(CommentStyle::Hash);
assert!(h.contains("# This file is auto-generated by alef"));
}
#[test]
fn test_header_block() {
let h = header(CommentStyle::Block);
assert!(h.starts_with("/*\n"));
assert!(h.contains(" * This file is auto-generated by alef"));
assert!(h.ends_with(" */\n"));
}
#[test]
fn test_inject_and_extract_rust() {
let h = header(CommentStyle::DoubleSlash);
let content = format!("{h}use foo;\n");
let hash = hash_content(&content);
let injected = inject_hash_line(&content, &hash);
assert!(injected.contains(HASH_PREFIX));
assert_eq!(extract_hash(&injected), Some(hash));
}
#[test]
fn test_inject_and_extract_python() {
let h = header(CommentStyle::Hash);
let content = format!("{h}import foo\n");
let hash = hash_content(&content);
let injected = inject_hash_line(&content, &hash);
assert!(injected.contains(&format!("# {HASH_PREFIX}")));
assert_eq!(extract_hash(&injected), Some(hash));
}
#[test]
fn test_inject_and_extract_c_block() {
let h = header(CommentStyle::Block);
let content = format!("{h}#include <stdio.h>\n");
let hash = hash_content(&content);
let injected = inject_hash_line(&content, &hash);
assert!(injected.contains(HASH_PREFIX));
assert!(
injected.contains(&format!(" * {HASH_PREFIX}")),
"expected ' * {HASH_PREFIX}' in block-comment header, got:\n{injected}"
);
assert!(
!injected.contains(&format!("// {HASH_PREFIX}")),
"block-comment header must not use '//' for the hash line, got:\n{injected}"
);
assert_eq!(extract_hash(&injected), Some(hash));
}
#[test]
fn test_inject_php_line2() {
let h = header(CommentStyle::DoubleSlash);
let content = format!("<?php\n{h}namespace Foo;\n");
let hash = hash_content(&content);
let injected = inject_hash_line(&content, &hash);
let lines: Vec<&str> = injected.lines().collect();
assert_eq!(lines[0], "<?php");
assert!(content_has_alef_marker(lines[1]));
assert!(lines.iter().any(|l| l.contains(HASH_PREFIX)));
assert_eq!(extract_hash(&injected), Some(hash));
}
#[test]
fn test_no_header_returns_unchanged() {
let content = "fn main() {}\n";
let injected = inject_hash_line(content, "abc123");
assert_eq!(injected, content);
assert_eq!(extract_hash(&injected), None);
}
#[test]
fn test_inject_and_extract_stamp_double_slash() {
let h = header(CommentStyle::DoubleSlash);
let content = format!("{h}use foo;\n");
let stamped = inject_stamp_line(&content, "handle-abi", "1");
assert!(stamped.contains("// alef:handle-abi:1"));
assert_eq!(extract_stamp(&stamped, "handle-abi").as_deref(), Some("1"));
}
#[test]
fn stamp_value_need_not_be_hex() {
let h = header(CommentStyle::Hash);
let content = format!("{h}import foo\n");
let stamped = inject_stamp_line(&content, "handle-abi", "not-a-hash-value");
assert_eq!(
extract_stamp(&stamped, "handle-abi").as_deref(),
Some("not-a-hash-value")
);
}
#[test]
fn stamp_and_hash_lines_coexist_and_extract_independently() {
let h = header(CommentStyle::DoubleSlash);
let content = format!("{h}use foo;\n");
let stamped = inject_stamp_line(&content, "handle-abi", "2");
let hash = hash_content(&stamped);
let finalized = inject_hash_line(&stamped, &hash);
assert_eq!(extract_stamp(&finalized, "handle-abi").as_deref(), Some("2"));
assert_eq!(extract_hash(&finalized), Some(hash));
}
#[test]
fn extract_stamp_does_not_match_a_different_key() {
let content = "// This file is auto-generated by alef — DO NOT EDIT.\n// alef:hash:abc123\nuse foo;\n";
assert_eq!(extract_stamp(content, "handle-abi"), None);
}
#[test]
fn extract_stamp_returns_none_without_a_header_marker() {
let content = "fn main() {}\n";
assert_eq!(inject_stamp_line(content, "handle-abi", "1"), content);
assert_eq!(extract_stamp(content, "handle-abi"), None);
}
#[test]
fn stamp_shapes_round_trip_across_comment_styles() {
let fixtures = [
(
"double slash",
"// auto-generated by alef\n// alef:handle-abi:1\nvalue\n",
),
("hash", "# auto-generated by alef\n# alef:handle-abi:1\nvalue\n"),
("block", "/* auto-generated by alef\n * alef:handle-abi:1\n */\nvalue\n"),
(
"html",
"<!-- auto-generated by alef -->\n<!-- alef:handle-abi:1 -->\nvalue\n",
),
];
for (name, content) in fixtures {
assert_eq!(extract_stamp(content, "handle-abi").as_deref(), Some("1"), "{name}");
}
}
#[test]
fn test_strip_hash_line() {
let content = "// auto-generated by alef\n// alef:hash:abc123\nuse foo;\n";
let stripped = strip_hash_line(content);
assert_eq!(stripped, "// auto-generated by alef\nuse foo;\n");
}
#[test]
fn test_roundtrip() {
let h = header(CommentStyle::Hash);
let original = format!("{h}import sys\n");
let hash = hash_content(&original);
let injected = inject_hash_line(&original, &hash);
let stripped = strip_hash_line(&injected);
assert_eq!(stripped, original);
assert_eq!(hash_content(&stripped), hash);
}
#[test]
fn test_content_has_alef_marker_true_without_hash_line() {
let content = "// This file is auto-generated by alef — DO NOT EDIT.\nfn hello() {}\n";
assert!(extract_hash(content).is_none(), "fixture must not carry a hash line");
assert!(
content_has_alef_marker(content),
"a headered file missing its hash must still be recognized as alef-owned"
);
}
#[test]
fn test_content_has_alef_marker_true_with_hash_line() {
let content = "// This file is auto-generated by alef — DO NOT EDIT.\n// alef:hash:abc123\nfn hello() {}\n";
assert!(content_has_alef_marker(content));
}
#[test]
fn test_content_has_alef_marker_true_for_alt_marker() {
let content = "// Generated by alef. DO NOT EDIT.\nfunc hello() {}\n";
assert!(content_has_alef_marker(content));
}
#[test]
fn test_content_has_alef_marker_false_for_user_owned_file() {
let content = "// hand-written helper\nfn hello() {}\n";
assert!(!content_has_alef_marker(content));
}
#[test]
fn test_content_has_alef_marker_false_when_marker_outside_scan_window() {
let padding = "// padding\n".repeat(MARKER_SCAN_LINES);
let content = format!("{padding}// This file is auto-generated by alef — DO NOT EDIT.\nfn hello() {{}}\n");
assert!(!content_has_alef_marker(&content));
}
#[test]
fn a_marker_at_the_deepest_legal_line_stamps_inside_polys_scan_window() {
let padding = "# padding\n".repeat(MARKER_SCAN_LINES - 1);
let content = format!("{padding}# This file is auto-generated by alef — DO NOT EDIT.\nkey = \"value\"\n");
assert!(
content_has_alef_marker(&content),
"control: a marker on line {MARKER_SCAN_LINES} must still be claimed, else this test \
measures nothing"
);
let stamped = inject_hash_line(&content, "abc123");
let stamp_line = stamped
.lines()
.position(|line| line.contains("alef:hash:"))
.map(|index| index + 1)
.expect("a claimed file must be stamped");
assert_eq!(
stamp_line,
deepest_hash_line(),
"the stamp must land on the line immediately after the marker"
);
assert!(
stamp_line <= POLY_GENERATED_SCAN_LINES,
"stamp landed on line {stamp_line}, past poly's {POLY_GENERATED_SCAN_LINES}-line window: \
alef would claim a file poly still reformats"
);
}
#[test]
fn test_compute_file_hash_distinct_content_yields_distinct_hashes() {
let a = compute_file_hash("fn one() {}\n");
let b = compute_file_hash("fn two() {}\n");
assert_ne!(a, b, "distinct file bodies must yield distinct embedded hashes");
}
#[test]
fn test_compute_file_hash_same_content_yields_same_hash() {
let a = compute_file_hash("fn one() {}\n");
let b = compute_file_hash("fn one() {}\n");
assert_eq!(a, b, "identical content must hash identically");
}
use std::path::{Path, PathBuf};
use tempfile::tempdir;
pub(super) fn write_file(dir: &Path, name: &str, content: &str) -> PathBuf {
let path = dir.join(name);
std::fs::write(&path, content).unwrap();
path
}
#[test]
fn sources_hash_changes_when_path_changes_even_if_content_same() {
let dir = tempdir().unwrap();
let s_a = write_file(dir.path(), "a.rs", "fn a() {}");
std::fs::create_dir_all(dir.path().join("moved")).unwrap();
let s_b = write_file(dir.path(), "moved/a.rs", "fn a() {}");
let h_a = compute_sources_hash(&[s_a]).unwrap();
let h_b = compute_sources_hash(&[s_b]).unwrap();
assert_ne!(
h_a, h_b,
"same content at a different path can produce different IR (rust_path differs)"
);
}
#[test]
fn sources_hash_errors_on_missing_source() {
let dir = tempdir().unwrap();
let bogus = dir.path().join("does-not-exist.rs");
assert!(compute_sources_hash(&[bogus]).is_err());
}
#[test]
fn sources_hash_stable_across_runs() {
let dir = tempdir().unwrap();
let s1 = write_file(dir.path(), "a.rs", "fn a() {}");
let s2 = write_file(dir.path(), "b.rs", "fn b() {}");
let sources = vec![s1, s2];
let h1 = compute_sources_hash(&sources).unwrap();
let h2 = compute_sources_hash(&sources).unwrap();
assert_eq!(h1, h2);
}
#[test]
fn sources_hash_path_order_independent() {
let dir = tempdir().unwrap();
let s1 = write_file(dir.path(), "a.rs", "fn a() {}");
let s2 = write_file(dir.path(), "b.rs", "fn b() {}");
let h_forward = compute_sources_hash(&[s1.clone(), s2.clone()]).unwrap();
let h_reverse = compute_sources_hash(&[s2, s1]).unwrap();
assert_eq!(h_forward, h_reverse);
}
mod marker_detection;
mod marker_negation;
mod marker_spellings;