use std::io::Cursor;
use std::path::Path;
use enprot::crypto::CryptoPolicyDefault;
use enprot::etree::{ParseOps, TextNode, parse};
fn make_paops() -> enprot::Result<ParseOps> {
ParseOps::new(Box::new(CryptoPolicyDefault {}))
}
fn fixture(name: &str) -> String {
let path = Path::new("tests/rsd-conformance/fixtures").join(name);
std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("failed to read {}: {}", path.display(), e))
}
fn parse_fixture(name: &str) -> enprot::Result<Vec<TextNode>> {
let input = fixture(name);
let mut paops = make_paops()?;
paops.runtime.fname = name.to_string();
let cursor = Cursor::new(input);
parse(cursor, &mut paops)
}
#[test]
fn fixture_01_basic_begin_end_parses() {
let tree = parse_fixture("01-basic-begin-end.ept").unwrap();
assert!(
tree.len() >= 2,
"expected at least 2 nodes, got {}",
tree.len()
);
let has_secret_block = tree
.iter()
.any(|n| matches!(n, TextNode::BeginEnd { keyw, .. } if keyw == "SECRET"));
assert!(has_secret_block, "expected a BeginEnd(SECRET) block");
}
#[test]
fn fixture_02_nested_blocks_parses() {
let tree = parse_fixture("02-nested-blocks.ept").unwrap();
let has_outer = tree
.iter()
.any(|n| matches!(n, TextNode::BeginEnd { keyw, .. } if keyw == "OUTER"));
assert!(has_outer, "expected a BeginEnd(OUTER) block");
let has_inner = tree.iter().any(|n| {
if let TextNode::BeginEnd { keyw, txt, .. } = n {
keyw == "OUTER"
&& txt
.iter()
.any(|c| matches!(c, TextNode::BeginEnd { keyw: k, .. } if k == "INNER"))
} else {
false
}
});
assert!(has_inner, "expected INNER nested inside OUTER");
}
#[test]
fn fixture_03_encrypted_inline_parses() {
let tree = parse_fixture("03-encrypted-inline.ept").unwrap();
let has_encrypted = tree
.iter()
.any(|n| matches!(n, TextNode::Encrypted { keyw, .. } if keyw == "SECRET"));
assert!(has_encrypted, "expected an Encrypted(SECRET) block");
let has_data = tree.iter().any(|n| {
if let TextNode::Encrypted { keyw, txt, .. } = n {
keyw == "SECRET" && txt.iter().any(|c| matches!(c, TextNode::Data(_)))
} else {
false
}
});
assert!(has_data, "expected DATA inside Encrypted(SECRET)");
}
#[test]
fn fixture_04_stored_pointer_parses() {
let tree = parse_fixture("04-stored-pointer.ept").unwrap();
let has_stored = tree
.iter()
.any(|n| matches!(n, TextNode::Stored { keyw, .. } if keyw == "SECRET"));
assert!(has_stored, "expected a Stored(SECRET) block");
}
#[test]
fn fixture_05_mismatched_end_rejected() {
let result = parse_fixture("05-malformed-mismatched-end.ept");
match result {
Err(_) => { }
Ok(tree) => {
assert!(
!tree.is_empty(),
"even malformed input should produce a tree"
);
}
}
}
#[test]
fn fixture_06_chain_anchor_parses() {
let tree = parse_fixture("06-chain-anchor.ept").unwrap();
let has_chain = tree.iter().any(|n| matches!(n, TextNode::Chain { .. }));
assert!(has_chain, "expected a Chain node");
let chain_ext = tree.iter().find_map(|n| {
if let TextNode::Chain { extfields } = n {
Some(extfields)
} else {
None
}
});
let ext = chain_ext.expect("Chain extfields");
assert!(
ext.contains_key("signer"),
"missing 'signer' in CHAIN extfields: {:?}",
ext.keys().collect::<Vec<_>>()
);
assert!(
ext.contains_key("payload"),
"missing 'payload' in CHAIN extfields"
);
assert!(ext.contains_key("sig"), "missing 'sig' in CHAIN extfields");
}
#[test]
fn fixture_07_immutable_content_parses() {
let tree = parse_fixture("07-immutable-content.ept").unwrap();
let has_immutable = tree
.iter()
.any(|n| matches!(n, TextNode::Immutable { name, .. } if name == "CONFIG"));
assert!(has_immutable, "expected an Immutable(CONFIG) block");
let imm = tree.iter().find_map(|n| match n {
TextNode::Immutable {
name,
hashalg,
hash,
..
} if name == "CONFIG" => Some((hashalg, hash)),
_ => None,
});
let (alg, hash) = imm.expect("Immutable fields");
assert_eq!(alg, "sha3-256");
assert_eq!(hash.len(), 64, "hash must be 64 hex chars (SHA3-256)");
}
#[test]
fn fixture_08_muted_sanitized_parses() {
let tree = parse_fixture("08-muted-sanitized.ept").unwrap();
let has_muted = tree.iter().any(|n| {
matches!(n, TextNode::Muted { name, hashalg, .. } if name == "SECRET" && hashalg == "sha3-256")
});
assert!(
has_muted,
"expected a Muted(SECRET) block with sha3-256 hashalg"
);
}
#[test]
fn fixture_09_conflict_markers_parse() {
let tree = parse_fixture("09-conflict-markers.ept").unwrap();
let has_conflict = tree
.iter()
.any(|n| matches!(n, TextNode::Conflict { keyw, .. } if keyw == "WORD_A"));
assert!(has_conflict, "expected a Conflict(WORD_A) block");
let conflict = tree.iter().find_map(|n| match n {
TextNode::Conflict { keyw, ours, theirs } if keyw == "WORD_A" => Some((ours, theirs)),
_ => None,
});
let (ours, theirs) = conflict.expect("Conflict body");
assert!(!ours.is_empty(), "expected `ours` side to have content");
assert!(!theirs.is_empty(), "expected `theirs` side to have content");
}
#[test]
fn fixture_10_include_manifest_parses() {
let tree = parse_fixture("10-include-manifest.ept").unwrap();
let manifest = tree.iter().find_map(|n| {
if let TextNode::BeginEnd { keyw, txt } = n {
if keyw == "MANIFEST" { Some(txt) } else { None }
} else {
None
}
});
let txt = manifest.expect("MANIFEST block");
let includes: Vec<&str> = txt
.iter()
.filter_map(|n| {
if let TextNode::Include { hash } = n {
Some(hash.as_str())
} else {
None
}
})
.collect();
assert_eq!(
includes.len(),
2,
"expected 2 INCLUDE directives in MANIFEST, got {}: {:?}",
includes.len(),
includes
);
assert!(
includes.iter().all(|h| h.len() == 64),
"INCLUDE hashes must be 64 hex chars: {:?}",
includes
);
}
#[test]
fn fixture_11_deeply_nested_parses() {
let tree = parse_fixture("11-deeply-nested.ept").unwrap();
let outer = tree.iter().find_map(|n| {
if let TextNode::BeginEnd { keyw, txt } = n {
if keyw == "OUTER" { Some(txt) } else { None }
} else {
None
}
});
let outer_txt = outer.expect("OUTER block");
let has_inner = outer_txt
.iter()
.any(|n| matches!(n, TextNode::BeginEnd { keyw, .. } if keyw == "INNER"));
assert!(has_inner, "expected INNER nested inside OUTER");
let sibling_inner = tree
.iter()
.any(|n| matches!(n, TextNode::BeginEnd { keyw, .. } if keyw == "INNER"));
let _ = sibling_inner;
}
#[test]
fn fixture_12_multiple_words_parses() {
let tree = parse_fixture("12-multiple-words.ept").unwrap();
let words: Vec<&str> = tree
.iter()
.filter_map(|n| {
if let TextNode::BeginEnd { keyw, .. } = n {
Some(keyw.as_str())
} else {
None
}
})
.collect();
assert_eq!(
words,
vec!["WORD_A", "WORD_B"],
"expected WORD_A and WORD_B blocks"
);
}
#[test]
fn fixture_13_multiline_data_parses() {
let tree = parse_fixture("13-multiline-data.ept").unwrap();
let encrypted = tree.iter().find_map(|n| {
if let TextNode::Encrypted { keyw, txt, .. } = n {
if keyw == "SECRET" { Some(txt) } else { None }
} else {
None
}
});
let txt = encrypted.expect("Encrypted(SECRET) block");
let data_nodes: Vec<_> = txt
.iter()
.filter_map(|n| {
if let TextNode::Data(d) = n {
Some(d)
} else {
None
}
})
.collect();
assert!(
!data_nodes.is_empty(),
"expected at least one Data node inside ENCRYPTED(SECRET)"
);
let total_bytes: usize = data_nodes.iter().map(|d| d.len()).sum();
assert!(
total_bytes >= 144,
"expected ≥ 144 bytes of concatenated DATA, got {total_bytes}"
);
}
#[test]
fn fixture_14_plain_only_parses() {
let tree = parse_fixture("14-plain-only.ept").unwrap();
assert!(!tree.is_empty(), "empty file should still produce a tree");
let all_plain = tree.iter().all(|n| matches!(n, TextNode::Plain(_)));
assert!(
all_plain,
"expected all nodes to be Plain in a markup-free file"
);
}
#[test]
fn fixture_15_key_cert_declarations_parse() {
let tree = parse_fixture("15-key-cert-declarations.ept").unwrap();
let has_key = tree
.iter()
.any(|n| matches!(n, TextNode::Key { name, .. } if name == "signing-key"));
assert!(has_key, "expected a Key(signing-key) declaration");
let has_cert = tree
.iter()
.any(|n| matches!(n, TextNode::Cert { name, .. } if name == "build-cert"));
assert!(has_cert, "expected a Cert(build-cert) declaration");
}
#[test]
fn fixture_16_mixed_directives_parse() {
let tree = parse_fixture("16-mixed-directives.ept").unwrap();
let has_stored = tree
.iter()
.any(|n| matches!(n, TextNode::Stored { keyw, .. } if keyw == "SECRET"));
assert!(has_stored, "expected a Stored(SECRET) block");
let has_encrypted = tree
.iter()
.any(|n| matches!(n, TextNode::Encrypted { keyw, .. } if keyw == "SECRET2"));
assert!(has_encrypted, "expected an Encrypted(SECRET2) block");
let has_chain = tree.iter().any(|n| matches!(n, TextNode::Chain { .. }));
assert!(has_chain, "expected a Chain anchor");
}
#[test]
fn fixture_17_empty_block_parses() {
let tree = parse_fixture("17-empty-block.ept").unwrap();
let has_empty = tree.iter().any(
|n| matches!(n, TextNode::BeginEnd { keyw, txt } if keyw == "EMPTY" && txt.is_empty()),
);
assert!(
has_empty,
"expected a BeginEnd(EMPTY) block with no children"
);
}
#[test]
fn fixture_18_comment_only_parses() {
let tree = parse_fixture("18-comment-only.ept").unwrap();
assert!(!tree.is_empty(), "expected at least one node");
let all_plain = tree.iter().all(|n| matches!(n, TextNode::Plain(_)));
assert!(
all_plain,
"expected all Plain nodes for a comment-only file"
);
}
#[test]
fn fixture_19_source_file_with_secrets_parses() {
let tree = parse_fixture("19-source-file-with-secrets.ept").unwrap();
let words: Vec<&str> = tree
.iter()
.filter_map(|n| {
if let TextNode::BeginEnd { keyw, .. } = n {
Some(keyw.as_str())
} else {
None
}
})
.collect();
assert!(
words.contains(&"API_KEY"),
"expected BeginEnd(API_KEY), got: {words:?}"
);
assert!(
words.contains(&"DB_PASSWORD"),
"expected BeginEnd(DB_PASSWORD), got: {words:?}"
);
}
#[test]
fn fixture_20_multiple_stored_parses() {
let tree = parse_fixture("20-multiple-stored.ept").unwrap();
let stored_words: Vec<&str> = tree
.iter()
.filter_map(|n| {
if let TextNode::Stored { keyw, .. } = n {
Some(keyw.as_str())
} else {
None
}
})
.collect();
assert_eq!(
stored_words,
vec!["SECRET", "CONFIG"],
"expected STORED(SECRET) and STORED(CONFIG)"
);
}
#[test]
fn fixture_21_custom_separators_parses() {
let tree = parse_fixture("21-custom-separators.ept").unwrap();
assert!(!tree.is_empty());
let all_plain = tree.iter().all(|n| matches!(n, TextNode::Plain(_)));
assert!(
all_plain,
"with default separators, custom-separator file should be all Plain"
);
}
#[test]
fn fixture_22_encrypted_minimal_parses() {
let tree = parse_fixture("22-encrypted-minimal.ept").unwrap();
let has_enc = tree
.iter()
.any(|n| matches!(n, TextNode::Encrypted { keyw, .. } if keyw == "SHORT"));
assert!(has_enc, "expected Encrypted(SHORT) block");
let has_plain = tree.iter().any(|n| matches!(n, TextNode::Plain(_)));
assert!(
has_plain,
"expected trailing Plain text after encrypted block"
);
}
#[test]
fn fixture_23_data_inside_begin_end_parses() {
let tree = parse_fixture("23-data-inside-begin-end.ept").unwrap();
let block = tree.iter().find_map(|n| {
if let TextNode::BeginEnd { keyw, txt } = n {
if keyw == "FILE_CONFIG" {
Some(txt)
} else {
None
}
} else {
None
}
});
let txt = block.expect("BeginEnd(FILE_CONFIG) block");
let has_data = txt.iter().any(|n| matches!(n, TextNode::Data(_)));
assert!(has_data, "expected DATA node inside BeginEnd(FILE_CONFIG)");
}
#[test]
fn fixture_24_unkey_directive_parses() {
let tree = parse_fixture("24-unkey-directive.ept").unwrap();
let has_unkey = tree
.iter()
.any(|n| matches!(n, TextNode::Unkey { name } if name == "signing-key"));
assert!(has_unkey, "expected Unkey(signing-key) directive");
let has_begin = tree
.iter()
.any(|n| matches!(n, TextNode::BeginEnd { keyw, .. } if keyw == "DATA"));
assert!(has_begin, "expected BeginEnd(DATA) after UNKEY");
}
#[test]
fn fixture_25_uncert_directive_parses() {
let tree = parse_fixture("25-uncert-directive.ept").unwrap();
let has_uncert = tree
.iter()
.any(|n| matches!(n, TextNode::Uncert { name } if name == "build-cert"));
assert!(has_uncert, "expected Uncert(build-cert) directive");
let has_begin = tree
.iter()
.any(|n| matches!(n, TextNode::BeginEnd { keyw, .. } if keyw == "PAYLOAD"));
assert!(has_begin, "expected BeginEnd(PAYLOAD) after UNCERT");
}