browser-instance / firm_lang /tests /parser_entity_tests.rs
harvesthealth's picture
Upload folder using huggingface_hub
f14b4e9 verified
Raw
History Blame Contribute Delete
19.6 kB
#[cfg(test)]
mod tests {
use chrono::{Datelike, Offset, Timelike};
use firm_lang::parser::dsl::{ParsedValue, parse_source};
#[test]
fn test_basic_entity_parsing() {
let source = r#"
person john_doe {
name = "John Doe"
age = 42
active = true
}
"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
assert_eq!(entities.len(), 1);
assert_eq!(entities[0].entity_type(), Some("person"));
assert_eq!(entities[0].id(), Some("john_doe"));
}
#[test]
fn test_multiline_string_with_intentional_indentation() {
let source = r#"contact test {
code = """
function example() {
if (true) {
return "nested";
}
}
"""
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
let field = &fields[0];
assert_eq!(field.id(), Some("code"));
assert_eq!(
field.value(),
Ok(ParsedValue::String(
"function example() {\n if (true) {\n return \"nested\";\n }\n}"
.to_string()
))
);
}
#[test]
fn test_multiline_string_single_line() {
let source = r#"contact test { note = """Just one line""" }"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
let field = &fields[0];
assert_eq!(field.id(), Some("note"));
assert_eq!(
field.value(),
Ok(ParsedValue::String("Just one line".to_string()))
);
}
#[test]
fn test_multiline_string_empty_lines() {
let source = r#"contact test {
text = """
First paragraph
Second paragraph after empty line
"""
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
let field = &fields[0];
assert_eq!(field.id(), Some("text"));
assert_eq!(
field.value(),
Ok(ParsedValue::String(
"First paragraph\n\nSecond paragraph after empty line".to_string()
))
);
}
#[test]
fn test_mixed_string_types() {
let source = r#"contact test {
name = "John Doe"
bio = """
Software engineer
Based in San Francisco
"""
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
assert_eq!(
fields[0].value(),
Ok(ParsedValue::String("John Doe".to_string()))
);
assert_eq!(
fields[1].value(),
Ok(ParsedValue::String(
"Software engineer\nBased in San Francisco".to_string()
))
);
}
#[test]
fn test_number_fields() {
let source = r#"contact test { age = 42, height = 5.9 }"#;
let parsed = parse_source(String::from(source), None).unwrap();
assert_eq!(
parsed.entities()[0].fields()[0].value(),
Ok(ParsedValue::Integer(42))
);
assert_eq!(
parsed.entities()[0].fields()[1].value(),
Ok(ParsedValue::Float(5.9))
);
}
#[test]
fn test_boolean_fields() {
let source = r#"contact test { active = true, verified = false }"#;
let parsed = parse_source(String::from(source), None).unwrap();
assert_eq!(
parsed.entities()[0].fields()[0].value(),
Ok(ParsedValue::Boolean(true))
);
assert_eq!(
parsed.entities()[0].fields()[1].value(),
Ok(ParsedValue::Boolean(false))
);
}
#[test]
fn test_currency_decimal_amount() {
let source = r#"contact test {
salary = 75000.50 USD
bonus = 5000.00 EUR
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
if let Ok(ParsedValue::Currency { amount, currency }) = &fields[0].value() {
assert_eq!(amount.to_string(), "75000.50");
assert_eq!(currency.code(), "USD");
} else {
panic!("Expected currency value");
}
if let Ok(ParsedValue::Currency { amount, currency }) = &fields[1].value() {
assert_eq!(amount.to_string(), "5000.00");
assert_eq!(currency.code(), "EUR");
} else {
panic!("Expected currency value");
}
}
#[test]
fn test_currency_integer_amount() {
let source = r#"contact test {
salary = 50000 USD
budget = 1000 DKK
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
if let Ok(ParsedValue::Currency { amount, currency }) = &fields[0].value() {
assert_eq!(amount.to_string(), "50000");
assert_eq!(currency.code(), "USD");
} else {
panic!("Expected currency value");
}
if let Ok(ParsedValue::Currency { amount, currency }) = &fields[1].value() {
assert_eq!(amount.to_string(), "1000");
assert_eq!(currency.code(), "DKK");
} else {
panic!("Expected currency value");
}
}
#[test]
fn test_multiple_currency_fields() {
let source = r#"project budget_2023 {
total_budget = 100000 USD
marketing_spend = 25000.50 USD
r_and_d = 40000 USD
operations = 15000.25 USD
contingency = 19999.25 USD
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
assert_eq!(entities.len(), 1);
let fields = entities[0].fields();
assert_eq!(fields.len(), 5);
for field in fields {
match field.value() {
Ok(ParsedValue::Currency {
amount: _,
currency,
}) => {
assert_eq!(currency.code(), "USD");
}
_ => panic!("Expected currency value"),
}
}
}
#[test]
fn test_entity_references() {
let source = r#"contact test {
manager = contact.john_doe
company = organization.acme_corp
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
assert_eq!(
fields[0].value(),
Ok(ParsedValue::EntityReference {
entity_type: "contact".to_string(),
entity_id: "john_doe".to_string()
})
);
assert_eq!(
fields[1].value(),
Ok(ParsedValue::EntityReference {
entity_type: "organization".to_string(),
entity_id: "acme_corp".to_string()
})
);
}
#[test]
fn test_field_references() {
let source = r#"contact test {
title = contact.john_doe.title
department = organization.acme_corp.department
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
assert_eq!(
fields[0].value(),
Ok(ParsedValue::FieldReference {
entity_type: "contact".to_string(),
entity_id: "john_doe".to_string(),
field_id: "title".to_string()
})
);
assert_eq!(
fields[1].value(),
Ok(ParsedValue::FieldReference {
entity_type: "organization".to_string(),
entity_id: "acme_corp".to_string(),
field_id: "department".to_string()
})
);
}
#[test]
fn test_simple_lists() {
let source = r#"contact test {
tags = ["work", "important", "urgent"]
scores = [85, 92, 78]
flags = [true, false, true]
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
assert_eq!(
fields[0].value(),
Ok(ParsedValue::List(vec![
ParsedValue::String("work".to_string()),
ParsedValue::String("important".to_string()),
ParsedValue::String("urgent".to_string())
]))
);
assert_eq!(
fields[1].value(),
Ok(ParsedValue::List(vec![
ParsedValue::Integer(85),
ParsedValue::Integer(92),
ParsedValue::Integer(78)
]))
);
assert_eq!(
fields[2].value(),
Ok(ParsedValue::List(vec![
ParsedValue::Boolean(true),
ParsedValue::Boolean(false),
ParsedValue::Boolean(true)
]))
);
}
#[test]
fn test_empty_lists() {
let source = r#"contact test { tags = [] }"#;
let parsed = parse_source(String::from(source), None).unwrap();
assert_eq!(
parsed.entities()[0].fields()[0].value(),
Ok(ParsedValue::List(vec![]))
);
}
#[test]
fn test_nested_lists() {
let source = r#"contact test {
matrix = [[1, 2, 3], [4, 5, 6]]
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
assert_eq!(
fields[0].value(),
Ok(ParsedValue::List(vec![
ParsedValue::List(vec![
ParsedValue::Integer(1),
ParsedValue::Integer(2),
ParsedValue::Integer(3)
]),
ParsedValue::List(vec![
ParsedValue::Integer(4),
ParsedValue::Integer(5),
ParsedValue::Integer(6)
])
]))
);
}
#[test]
fn test_date_fields() {
let source = r#"contact test {
birthday = 1990-05-15
start_date = 2023-01-01
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
if let Ok(ParsedValue::DateTime(dt)) = &fields[0].value() {
assert_eq!(dt.naive_local().date().year(), 1990);
assert_eq!(dt.naive_local().date().month(), 5);
assert_eq!(dt.naive_local().date().day(), 15);
} else {
panic!("Expected date value");
}
if let Ok(ParsedValue::DateTime(dt)) = &fields[1].value() {
assert_eq!(dt.naive_local().date().year(), 2023);
assert_eq!(dt.naive_local().date().month(), 1);
assert_eq!(dt.naive_local().date().day(), 1);
} else {
panic!("Expected date value");
}
}
#[test]
fn test_multiple_date_fields() {
let source = r#"project timeline {
start_date = 2023-01-15
phase_1_end = 2023-03-31
phase_2_end = 2023-06-30
final_deadline = 2023-12-31
review_date = 2024-01-15
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
assert_eq!(entities.len(), 1);
let fields = entities[0].fields();
assert_eq!(fields.len(), 5);
// All fields should be dates
for field in fields {
assert!(matches!(field.value(), Ok(ParsedValue::DateTime(_))));
}
}
#[test]
fn test_datetime_local_timezone() {
let source = r#"contact test {
meeting = 2025-01-15 at 14:30 UTC+2
deadline = 2025-02-28 at 23:59 UTC-5
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
if let Ok(ParsedValue::DateTime(dt)) = &fields[0].value() {
assert_eq!(dt.naive_local().date().year(), 2025);
assert_eq!(dt.naive_local().date().month(), 1);
assert_eq!(dt.naive_local().date().day(), 15);
assert_eq!(dt.naive_local().time().hour(), 14);
assert_eq!(dt.naive_local().time().minute(), 30);
assert_eq!(dt.offset().fix().local_minus_utc(), 2 * 3600);
} else {
panic!("Expected datetime value");
}
if let Ok(ParsedValue::DateTime(dt)) = &fields[1].value() {
assert_eq!(dt.naive_local().date().year(), 2025);
assert_eq!(dt.naive_local().date().month(), 2);
assert_eq!(dt.naive_local().date().day(), 28);
assert_eq!(dt.naive_local().time().hour(), 23);
assert_eq!(dt.naive_local().time().minute(), 59);
assert_eq!(dt.offset().fix().local_minus_utc(), -5 * 3600);
} else {
panic!("Expected datetime value");
}
}
#[test]
fn test_datetime_single_digit_hour() {
let source = r#"contact test {
meeting = 2025-01-15 at 9:30
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
if let Ok(ParsedValue::DateTime(dt)) = &fields[0].value() {
assert_eq!(dt.naive_local().date().year(), 2025);
assert_eq!(dt.naive_local().date().month(), 1);
assert_eq!(dt.naive_local().date().day(), 15);
assert_eq!(dt.naive_local().time().hour(), 9);
assert_eq!(dt.naive_local().time().minute(), 30);
} else {
panic!("Expected datetime value");
}
}
#[test]
fn test_datetime_utc_negative_offset() {
let source = r#"contact test {
meeting = 2025-01-15 at 14:30 UTC-7
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
if let Ok(ParsedValue::DateTime(dt)) = &fields[0].value() {
assert_eq!(dt.offset().fix().local_minus_utc(), -7 * 3600);
} else {
panic!("Expected datetime value");
}
}
#[test]
fn test_datetime_utc_positive_offset() {
let source = r#"contact test {
meeting = 2025-01-15 at 14:30 UTC+3
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
if let Ok(ParsedValue::DateTime(dt)) = &fields[0].value() {
assert_eq!(dt.offset().fix().local_minus_utc(), 3 * 3600);
} else {
panic!("Expected datetime value");
}
}
#[test]
fn test_datetime_utc() {
let source = r#"contact test {
meeting = 2025-01-15 at 14:30 UTC
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
if let Ok(ParsedValue::DateTime(dt)) = &fields[0].value() {
assert_eq!(dt.offset().fix().local_minus_utc(), 0);
} else {
panic!("Expected datetime value");
}
}
#[test]
fn test_heterogeneous_list_error() {
let source = r#"contact test {
mixed = ["string", 42]
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
match fields[0].value() {
Err(_) => {
// Expected error for heterogeneous list
}
Ok(_) => panic!("Expected error for heterogeneous list"),
}
}
#[test]
fn test_invalid_boolean_error() {
let source = r#"contact test { flag = maybe }"#;
let parsed = parse_source(String::from(source), None).unwrap();
assert!(parsed.has_error());
}
#[test]
fn test_invalid_integer_error() {
let source = r#"contact test { age = 42abc }"#;
let parsed = parse_source(String::from(source), None).unwrap();
assert!(parsed.has_error());
}
#[test]
fn test_invalid_float_error() {
let source = r#"contact test { height = 5.9.2 }"#;
let parsed = parse_source(String::from(source), None).unwrap();
assert!(parsed.has_error());
}
#[test]
fn test_invalid_currency_format_error() {
let source = r#"contact test { salary = 50000USD }"#;
let parsed = parse_source(String::from(source), None).unwrap();
assert!(parsed.has_error());
}
#[test]
fn test_invalid_currency_amount_error() {
let source = r#"contact test { salary = abc USD }"#;
let parsed = parse_source(String::from(source), None).unwrap();
assert!(parsed.has_error());
}
#[test]
fn test_invalid_currency_code_error() {
let source = r#"contact test {
salary = 50000 DOLLARS
bonus = 5000 US
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
assert!(parsed.has_error());
}
#[test]
fn test_invalid_reference_format_error() {
let source = r#"contact test { ref = contact.too.many.parts.here }"#;
let parsed = parse_source(String::from(source), None).unwrap();
assert!(parsed.has_error());
}
#[test]
fn test_invalid_date_error() {
let source = r#"contact test {
bad_date1 = 2023-13-01
bad_date2 = 2023-02-30
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
assert!(fields[0].value().is_err());
assert!(fields[1].value().is_err());
}
#[test]
fn test_invalid_datetime_error() {
let source = r#"contact test {
bad_datetime1 = 2023-13-01 at 14:30
bad_datetime2 = 2023-01-01 at 25:30
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
assert!(fields[0].value().is_err());
assert!(fields[1].value().is_err());
}
#[test]
fn test_invalid_timezone_error() {
let source = r#"contact test {
bad_tz = 2023-01-01 at 14:30 UTC+25
}"#;
let parsed = parse_source(String::from(source), None).unwrap();
let entities = parsed.entities();
let fields = entities[0].fields();
assert!(fields[0].value().is_err());
}
}