mod common;
use bc_envelope::prelude::*;
use bc_envelope_pattern::{Matcher, Pattern, format_paths};
use indoc::indoc;
#[test]
fn test_subject_pattern() {
let envelope = Envelope::new("Alice");
let any_subject_pat = Pattern::any_subject();
let matching_paths = any_subject_pat.paths(&envelope);
#[rustfmt::skip]
let expected_1 = indoc! {r#"
13941b48 LEAF "Alice"
"#}.trim();
assert_actual_expected!(format_paths(&matching_paths), expected_1);
let envelope_with_assertions = envelope.add_assertion("knows", "Bob");
let matching_paths = any_subject_pat.paths(&envelope_with_assertions);
#[rustfmt::skip]
let expected_2 = indoc! {r#"
8955db5e NODE "Alice" [ "knows": "Bob" ]
13941b48 LEAF "Alice"
"#}.trim();
assert_actual_expected!(format_paths(&matching_paths), expected_2);
let text_subject_pat = Pattern::subject(Pattern::any_text());
let matching_paths = text_subject_pat.paths(&envelope);
assert_actual_expected!(format_paths(&matching_paths), expected_1);
let matching_paths = text_subject_pat.paths(&envelope_with_assertions);
assert_actual_expected!(format_paths(&matching_paths), expected_2);
let number_subject_pat = Pattern::subject(Pattern::any_number());
let matching_paths = number_subject_pat.paths(&envelope);
assert!(matching_paths.is_empty());
let matching_paths = number_subject_pat.paths(&envelope_with_assertions);
assert!(matching_paths.is_empty());
}
#[test]
fn test_wrapped_pattern() {
let envelope = Envelope::new(42);
assert!(!Pattern::wrapped().matches(&envelope));
let wrapped_envelope = envelope.wrap();
let paths = Pattern::unwrap().paths(&wrapped_envelope);
#[rustfmt::skip]
let expected = indoc! {r#"
58b1ac6a WRAPPED { 42 }
7f83f7bd LEAF 42
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
let wrapped_envelope_with_assertion =
wrapped_envelope.add_assertion("an", "assertion");
let paths = Pattern::unwrap().paths(&wrapped_envelope_with_assertion);
#[rustfmt::skip]
let expected = indoc! {r#"
169aba00 NODE { 42 } [ "an": "assertion" ]
7f83f7bd LEAF 42
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
let wrapped_twice = wrapped_envelope_with_assertion.wrap();
let paths = Pattern::unwrap().paths(&wrapped_twice);
#[rustfmt::skip]
let expected = indoc! {r#"
52d47c15 WRAPPED { { 42 } [ "an": "assertion" ] }
169aba00 NODE { 42 } [ "an": "assertion" ]
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
let wrapped_twice_pattern =
Pattern::traverse(vec![Pattern::unwrap(), Pattern::unwrap()]);
let paths = wrapped_twice_pattern.paths(&wrapped_twice);
#[rustfmt::skip]
let expected = indoc! {r#"
52d47c15 WRAPPED { { 42 } [ "an": "assertion" ] }
169aba00 NODE { 42 } [ "an": "assertion" ]
7f83f7bd LEAF 42
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
}
#[test]
fn test_assertion_pattern() {
let envelope_without_assertions = Envelope::new("Alice");
assert!(!Pattern::any_assertion().matches(&envelope_without_assertions));
let envelope_with_assertions = envelope_without_assertions
.add_assertion("knows", "Bob")
.add_assertion("worksWith", "Charlie");
let paths = Pattern::any_assertion().paths(&envelope_with_assertions);
#[rustfmt::skip]
let expected = indoc! {r#"
78d666eb ASSERTION "knows": "Bob"
c269cf0a ASSERTION "worksWith": "Charlie"
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
}
#[test]
fn test_assertion_predicate_pattern() {
let envelope = Envelope::new("Alice")
.add_assertion("knows", "Bob")
.add_assertion("knows", "Charlie")
.add_assertion("worksWith", "David");
let paths = Pattern::assertion_with_predicate(Pattern::text("knows"))
.paths(&envelope);
#[rustfmt::skip]
let expected = indoc! {r#"
78d666eb ASSERTION "knows": "Bob"
7af83724 ASSERTION "knows": "Charlie"
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
let pattern = Pattern::traverse(vec![
Pattern::assertion_with_predicate(Pattern::text("knows")),
Pattern::any_object(),
]);
let paths = pattern.paths(&envelope);
#[rustfmt::skip]
let expected = indoc! {r#"
78d666eb ASSERTION "knows": "Bob"
13b74194 LEAF "Bob"
7af83724 ASSERTION "knows": "Charlie"
ee8e3b02 LEAF "Charlie"
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
}
#[test]
fn test_digest_pattern() {
let envelope = Envelope::new("Hello, World!");
let digest = envelope.digest();
let prefix = digest.data()[..4].to_vec();
assert!(Pattern::digest(digest).matches(&envelope));
assert!(!Pattern::digest(Digest::from_data([0; 32])).matches(&envelope));
assert!(Pattern::digest_prefix(prefix).matches(&envelope));
assert!(
!Pattern::digest_prefix(vec![0xff, 0xff, 0xff, 0xff])
.matches(&envelope)
);
let envelope_with_assertions =
envelope.clone().add_assertion("test", "value");
let digest_with_assertions = envelope_with_assertions.digest();
assert!(!Pattern::digest(digest).matches(&envelope_with_assertions));
assert!(
Pattern::digest(digest_with_assertions)
.matches(&envelope_with_assertions)
);
let paths = Pattern::digest(digest).paths(&envelope);
#[rustfmt::skip]
let expected = indoc! {r#"
0a852327 LEAF "Hello, World!"
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
let paths = Pattern::digest(Digest::from_data([0; 32])).paths(&envelope);
assert!(paths.is_empty());
}
#[test]
fn test_digest_pattern_binary_regex() {
let envelope = Envelope::new("Hello, World!");
let digest = envelope.digest();
let digest_bytes = digest.data();
let any_32_bytes_regex =
regex::bytes::Regex::new(r"(?s-u)^.{32}$").unwrap();
assert!(
Pattern::digest_binary_regex(any_32_bytes_regex).matches(&envelope)
);
let all_zeros_regex =
regex::bytes::Regex::new(r"(?s-u)^\x00{32}$").unwrap();
assert!(!Pattern::digest_binary_regex(all_zeros_regex).matches(&envelope));
let first_byte_hex = format!(r"(?s-u)^\x{:02x}", digest_bytes[0]);
let starts_with_first_byte =
regex::bytes::Regex::new(&first_byte_hex).unwrap();
assert!(
Pattern::digest_binary_regex(starts_with_first_byte).matches(&envelope)
);
let first_two_bytes_pattern =
format!(r"(?s-u)^\x{:02x}\x{:02x}", digest_bytes[0], digest_bytes[1]);
let exact_first_two =
regex::bytes::Regex::new(&first_two_bytes_pattern).unwrap();
assert!(Pattern::digest_binary_regex(exact_first_two).matches(&envelope));
let any_byte_range =
regex::bytes::Regex::new(r"(?s-u)[\x00-\xFF]").unwrap();
assert!(Pattern::digest_binary_regex(any_byte_range).matches(&envelope));
let non_zero_start =
regex::bytes::Regex::new(r"(?s-u)^[\x01-\xFF]").unwrap();
let should_match = digest_bytes[0] != 0;
assert_eq!(
Pattern::digest_binary_regex(non_zero_start).matches(&envelope),
should_match
);
let paths = Pattern::digest_binary_regex(
regex::bytes::Regex::new(r"(?s-u)^.{32}$").unwrap(),
)
.paths(&envelope);
#[rustfmt::skip]
let expected = indoc! {r#"
0a852327 LEAF "Hello, World!"
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
let paths = Pattern::digest_binary_regex(
regex::bytes::Regex::new(r"(?s-u)^\x00{32}$").unwrap(),
)
.paths(&envelope);
assert!(paths.is_empty());
let envelope2 = Envelope::new("Different content");
let digest2 = envelope2.digest();
assert_ne!(digest.data(), digest2.data());
let any_digest_regex = regex::bytes::Regex::new(r"(?s-u)^.{32}$").unwrap();
assert!(
Pattern::digest_binary_regex(any_digest_regex.clone())
.matches(&envelope)
);
assert!(Pattern::digest_binary_regex(any_digest_regex).matches(&envelope2));
let contains_aa = regex::bytes::Regex::new(r"(?s-u)\xaa").unwrap();
let envelope1_has_aa = digest_bytes.contains(&0xaa);
assert_eq!(
Pattern::digest_binary_regex(contains_aa.clone()).matches(&envelope),
envelope1_has_aa
);
let digest2_bytes = digest2.data();
let envelope2_has_aa = digest2_bytes.contains(&0xaa);
assert_eq!(
Pattern::digest_binary_regex(contains_aa).matches(&envelope2),
envelope2_has_aa
);
let pattern_32_ascending =
regex::bytes::Regex::new(r"(?s-u)^[\x00-\x1F]{32}$").unwrap();
let has_low_bytes = digest_bytes.iter().all(|&b| b <= 0x1F);
assert_eq!(
Pattern::digest_binary_regex(pattern_32_ascending).matches(&envelope),
has_low_bytes
);
let pattern_high_bit =
regex::bytes::Regex::new(r"(?s-u)[\x80-\xFF]").unwrap();
let has_high_bit = digest_bytes.iter().any(|&b| b >= 0x80);
assert_eq!(
Pattern::digest_binary_regex(pattern_high_bit).matches(&envelope),
has_high_bit
);
let envelope_with_assertions =
envelope.clone().add_assertion("test", "value");
let digest_with_assertions = envelope_with_assertions.digest();
let universal_pattern = regex::bytes::Regex::new(r"(?s-u)^.{32}$").unwrap();
assert!(
Pattern::digest_binary_regex(universal_pattern)
.matches(&envelope_with_assertions)
);
assert_ne!(digest.data(), digest_with_assertions.data());
}
#[test]
fn test_node_pattern() {
let leaf_envelope = Envelope::new("Just a leaf");
assert!(!Pattern::any_node().matches(&leaf_envelope));
assert!(
!Pattern::node_with_assertions_range(1..=3).matches(&leaf_envelope)
);
assert!(!Pattern::node_with_assertions_count(1).matches(&leaf_envelope));
let single_assertion_envelope =
Envelope::new("Alice").add_assertion("knows", "Bob");
assert!(Pattern::any_node().matches(&single_assertion_envelope));
assert!(
Pattern::node_with_assertions_range(1..=3)
.matches(&single_assertion_envelope)
);
assert!(
Pattern::node_with_assertions_count(1)
.matches(&single_assertion_envelope)
);
assert!(
!Pattern::node_with_assertions_count(2)
.matches(&single_assertion_envelope)
);
let multi_assertion_envelope = single_assertion_envelope
.add_assertion("age", 25)
.add_assertion("city", "New York");
assert!(Pattern::any_node().matches(&multi_assertion_envelope));
assert!(
Pattern::node_with_assertions_range(1..=5)
.matches(&multi_assertion_envelope)
);
assert!(
Pattern::node_with_assertions_count(3)
.matches(&multi_assertion_envelope)
);
assert!(
!Pattern::node_with_assertions_count(2)
.matches(&multi_assertion_envelope)
);
assert!(
!Pattern::node_with_assertions_range(4..=5)
.matches(&multi_assertion_envelope)
);
let paths = Pattern::any_node().paths(&single_assertion_envelope);
#[rustfmt::skip]
let expected = indoc! {r#"
8955db5e NODE "Alice" [ "knows": "Bob" ]
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
}
#[test]
fn test_obscured_pattern() {
let original_envelope = Envelope::new("Secret data");
let elided_envelope = original_envelope.elide();
assert!(Pattern::obscured().matches(&elided_envelope));
assert!(Pattern::elided().matches(&elided_envelope));
assert!(!Pattern::encrypted().matches(&elided_envelope));
assert!(!Pattern::compressed().matches(&elided_envelope));
assert!(!Pattern::obscured().matches(&original_envelope));
assert!(!Pattern::elided().matches(&original_envelope));
assert!(!Pattern::encrypted().matches(&original_envelope));
assert!(!Pattern::compressed().matches(&original_envelope));
let paths = Pattern::elided().paths(&elided_envelope);
#[rustfmt::skip]
let expected = format!(
"{} ELIDED",
elided_envelope.short_id(DigestDisplayFormat::Short)
);
assert_actual_expected!(format_paths(&paths), expected);
let paths = Pattern::elided().paths(&original_envelope);
assert!(paths.is_empty());
{
use bc_components::SymmetricKey;
let key = SymmetricKey::new();
let encrypted_envelope =
original_envelope.encrypt_subject(&key).unwrap();
assert!(Pattern::obscured().matches(&encrypted_envelope));
assert!(Pattern::encrypted().matches(&encrypted_envelope));
assert!(!Pattern::elided().matches(&encrypted_envelope));
assert!(!Pattern::compressed().matches(&encrypted_envelope));
}
{
let compressed_envelope = original_envelope.compress().unwrap();
assert!(Pattern::obscured().matches(&compressed_envelope));
assert!(Pattern::compressed().matches(&compressed_envelope));
assert!(!Pattern::elided().matches(&compressed_envelope));
assert!(!Pattern::encrypted().matches(&compressed_envelope));
}
}