#[cfg(test)]
const WINDOWS_PATH_SPELLINGS: &[&str] = &[
r"src\lib.rs",
r"src\hashline\release\mod.rs",
r"C:\Users\agent\project\src\main.rs",
r"\\?\C:\repo\file.rs",
];
#[cfg(test)]
mod tests {
use super::WINDOWS_PATH_SPELLINGS;
use crate::hashline::oracle::tag_for;
use crate::hashline::release::fixture::{
build_a13_fixture, build_a13_fixture_crlf, normalize_agent_path_spelling,
};
use crate::hashline::scan::{scan_bytes, TerminatorKind};
use crate::hashline::snapshot::{render_tagged_snapshot, render_tagless_snapshot};
use crate::hashline::syntax::{parse_hashline_patch, HashlineRejectionCode, RejectionStage};
#[test]
fn crlf_records_preserve_terminator_kind_and_differ_from_lf_tags() {
let lf = build_a13_fixture();
let crlf = build_a13_fixture_crlf();
let lf_snap = scan_bytes(&lf);
let crlf_snap = scan_bytes(&crlf);
let lf_term = lf_snap
.records
.values()
.next()
.expect("lf fixture has records")
.terminator;
let crlf_term = crlf_snap
.records
.values()
.next()
.expect("crlf fixture has records")
.terminator;
assert_eq!(lf_term, TerminatorKind::Lf);
assert_eq!(crlf_term, TerminatorKind::CrLf);
let lf_tag = tag_for(&lf);
let crlf_tag = tag_for(&crlf);
assert_eq!(
lf_tag, crlf_tag,
"tag normalization strips CR before LF, so LF/CRLF twins share a tag"
);
assert_ne!(lf_snap.records.values().next().map(|r| r.terminator), None);
assert_eq!(crlf_term, TerminatorKind::CrLf);
}
#[test]
fn crlf_render_paths_emit_stable_carriers_for_both_modes() {
let crlf = build_a13_fixture_crlf();
let head: Vec<u8> = crlf.iter().copied().take(4 * 1024).collect();
let snapshot = scan_bytes(&head);
let path = r"src\fixture\a13_crlf.txt";
let tagged = render_tagged_snapshot(&snapshot, path);
let tagless = render_tagless_snapshot(&snapshot, path);
assert!(
tagged
.text
.starts_with(&format!("[{path}#{}]", snapshot.tag)),
"gate-on carrier must preserve the requested path spelling verbatim: {}",
&tagged.text[..tagged.text.find('\n').unwrap_or(80)]
);
assert!(
tagless.text.contains("1:"),
"gate-off render must still emit absolute gutters"
);
assert_eq!(tagged.requested_path, path);
assert_eq!(tagless.requested_path, path);
}
#[test]
fn windows_path_spellings_normalize_for_comparison_helpers() {
for spelling in WINDOWS_PATH_SPELLINGS {
let normalized = normalize_agent_path_spelling(spelling);
assert!(
!normalized.contains('\\'),
"normalized spelling must not retain backslashes: {normalized}"
);
assert!(
normalized.contains('/'),
"normalized spelling must use forward slashes: {normalized}"
);
}
}
#[test]
fn parser_accepts_forward_slash_headers_and_rejects_malformed_tags_identically() {
let lf_patch = "[src/lib.rs#CAFE]\nREM 1\n";
let crlf_patch = "[src/lib.rs#CAFE]\r\nREM 1\r\n";
let lf = parse_hashline_patch(lf_patch);
let crlf = parse_hashline_patch(crlf_patch);
match (lf, crlf) {
(Ok(_), Ok(_)) => {}
(Err(left), Err(right)) => {
assert_eq!(left.code, right.code);
assert_eq!(left.stage, right.stage);
}
(left, right) => panic!("LF/CRLF patch adjudication diverged: {left:?} vs {right:?}"),
}
let malformed = parse_hashline_patch("[src/lib.rs#CAF]\nREM 1\n");
let err = malformed.expect_err("three-hex tag is malformed");
assert_eq!(err.code, HashlineRejectionCode::MalformedTag);
assert_eq!(err.stage, RejectionStage::Header);
}
#[test]
fn mixed_terminator_fixture_retains_per_record_kinds() {
let bytes = b"lf-only\ncrlf-line\r\nnone-at-eof";
let snapshot = scan_bytes(bytes);
let kinds: Vec<_> = snapshot
.records
.values()
.map(|record| record.terminator)
.collect();
assert_eq!(
kinds,
vec![
TerminatorKind::Lf,
TerminatorKind::CrLf,
TerminatorKind::None
]
);
}
}