#[cfg(test)]
mod tests1 {
use crate::command_interpreter::task_parser::{
Value, parse_document, parse_document_as_strings, parse_document_with_template, parse_key,
parse_key_value_pair, parse_section, parse_this_sections, parse_title, parse_value,
parse_value_list,
};
use std::collections::HashMap;
#[test]
fn test_parse_title() {
let input = "title1\n key1: value1";
let (title, remaining) = parse_title(input, input).unwrap();
assert_eq!(title, "title1");
assert_eq!(remaining, "key1: value1");
let input = "title_with_underscore key1: value1";
let (title, remaining) = parse_title(input, input).unwrap();
assert_eq!(title, "title_with_underscore");
assert_eq!(remaining, "key1: value1");
let input = "title123 key1: value1";
let (title, remaining) = parse_title(input, input).unwrap();
assert_eq!(title, "title123");
assert_eq!(remaining, "key1: value1");
}
#[test]
fn test_parse_key() {
let input = "key1: value1";
let (key, remaining) = parse_key(input, input).unwrap();
assert_eq!(key, "key1");
assert_eq!(remaining, ": value1");
let input = "key_with_underscore: value1";
let (key, remaining) = parse_key(input, input).unwrap();
assert_eq!(key, "key_with_underscore");
assert_eq!(remaining, ": value1");
let input = "key123: value1";
let (key, remaining) = parse_key(input, input).unwrap();
assert_eq!(key, "key123");
assert_eq!(remaining, ": value1");
}
#[test]
fn test_parse_value() {
let input = "value1, value2";
let (value, remaining) = parse_value(input, input).unwrap();
assert_eq!(value, Value::String("value1".to_string()));
assert_eq!(remaining, ", value2");
let input = "123, next";
let (value, remaining) = parse_value(input, input).unwrap();
assert_eq!(value, Value::Integer(123));
assert_eq!(remaining, ", next");
let input = "123.45, next";
let (value, remaining) = parse_value(input, input).unwrap();
assert_eq!(value, Value::Float(123.45));
assert_eq!(remaining, ", next");
let input = "true, next";
let (value, remaining) = parse_value(input, input).unwrap();
assert_eq!(value, Value::Boolean(true));
assert_eq!(remaining, ", next");
}
#[test]
fn test_parse_value_vectors() {
let input = "[1.0,2.5,3.14], next";
let (value, remaining) = parse_value(input, input).unwrap();
assert_eq!(value, Value::Vector(vec![1.0, 2.5, 3.14]));
assert_eq!(remaining, ", next");
let input = "[], next";
let (value, remaining) = parse_value(input, input).unwrap();
assert_eq!(value, Value::Vector(vec![]));
assert_eq!(remaining, ", next");
let input = "[1.0, 2.5, 3.14], next";
let (value, remaining) = parse_value(input, input).unwrap();
assert_eq!(value, Value::Vector(vec![1.0, 2.5, 3.14]));
assert_eq!(remaining, ", next");
}
#[test]
fn test_parse_value_options() {
let input = "None, next";
let (value, remaining) = parse_value(input, input).unwrap();
assert_eq!(value, Value::Optional(None));
assert_eq!(remaining, ", next");
let input = "Some(42), next";
let (value, remaining) = parse_value(input, input).unwrap();
assert_eq!(value, Value::Optional(Some(Box::new(Value::Integer(42)))));
assert_eq!(remaining, ", next");
let input = "Some(3.14), next";
let (value, remaining) = parse_value(input, input).unwrap();
assert_eq!(value, Value::Optional(Some(Box::new(Value::Float(3.14)))));
assert_eq!(remaining, ", next");
let input = "Some(hello), next";
let (value, remaining) = parse_value(input, input).unwrap();
assert_eq!(
value,
Value::Optional(Some(Box::new(Value::String("hello".to_string()))))
);
assert_eq!(remaining, ", next");
let input = "Some(true), next";
let (value, remaining) = parse_value(input, input).unwrap();
assert_eq!(value, Value::Optional(Some(Box::new(Value::Boolean(true)))));
assert_eq!(remaining, ", next");
}
#[test]
fn test_parse_value_list() {
let input = "value1, 123, 45.67, true";
let (values, remaining) = parse_value_list(input, input).unwrap();
assert_eq!(
values,
vec![
Value::String("value1".to_string()),
Value::Integer(123),
Value::Float(45.67),
Value::Boolean(true)
]
);
assert_eq!(remaining, "");
let input = "value1";
let (values, remaining) = parse_value_list(input, input).unwrap();
assert_eq!(values, vec![Value::String("value1".to_string())]);
assert_eq!(remaining, "");
let input = "";
let result = parse_value_list(input, input);
assert!(result.is_err());
}
#[test]
fn test_parse_value_list_with_vectors_and_options() {
let input = "[1.0,2.0], None, Some(42), Some(hello)";
let (values, remaining) = parse_value_list(input, input).unwrap();
assert_eq!(
values,
vec![
Value::Vector(vec![1.0, 2.0]),
Value::Optional(None),
Value::Optional(Some(Box::new(Value::Integer(42)))),
Value::Optional(Some(Box::new(Value::String("hello".to_string()))))
]
);
assert_eq!(remaining, "");
}
#[test]
fn test_parse_key_value_pair() {
let input = "key1: value1, value2";
let ((key, values), remaining) = parse_key_value_pair(input, input).unwrap();
assert_eq!(key, "key1");
assert_eq!(
values,
vec![
Value::String("value1".to_string()),
Value::String("value2".to_string())
]
);
assert_eq!(remaining, "");
let input = "key1: value1, 123, 45.67, true";
let ((key, values), remaining) = parse_key_value_pair(input, input).unwrap();
assert_eq!(key, "key1");
assert_eq!(
values,
vec![
Value::String("value1".to_string()),
Value::Integer(123),
Value::Float(45.67),
Value::Boolean(true)
]
);
assert_eq!(remaining, "");
let input = "key1: value1, value2;";
let ((key, values), remaining) = parse_key_value_pair(input, input).unwrap();
assert_eq!(key, "key1");
assert_eq!(
values,
vec![
Value::String("value1".to_string()),
Value::String("value2".to_string())
]
);
assert_eq!(remaining, ";");
let input = "key1 : value1 , value2";
let ((key, values), remaining) = parse_key_value_pair(input, input).unwrap();
assert_eq!(key, "key1");
assert_eq!(
values,
vec![
Value::String("value1".to_string()),
Value::String("value2".to_string())
]
);
assert_eq!(remaining, "");
let input = "key1: value1";
let ((key, values), remaining) = parse_key_value_pair(input, input).unwrap();
assert_eq!(key, "key1");
assert_eq!(values, vec![Value::String("value1".to_string())]);
assert_eq!(remaining, "");
let input = "key1:";
let result = parse_key_value_pair(input, input);
assert!(result.is_err());
}
#[test]
fn test_parse_section() {
let input = "section1 key1: value1, value2 key2: value3, value4";
let ((title, map), remaining) = parse_section(input, input).unwrap();
assert_eq!(title, "section1");
assert_eq!(map.len(), 2);
assert_eq!(
map.get("key1").unwrap(),
&vec![
Value::String("value1".to_string()),
Value::String("value2".to_string())
]
);
assert_eq!(
map.get("key2").unwrap(),
&vec![
Value::String("value3".to_string()),
Value::String("value4".to_string())
]
);
assert_eq!(remaining, "");
let input = "section1 key1: value1, 123 key2: 45.67, true";
let ((title, map), remaining) = parse_section(input, input).unwrap();
assert_eq!(title, "section1");
assert_eq!(map.len(), 2);
assert_eq!(
map.get("key1").unwrap(),
&vec![Value::String("value1".to_string()), Value::Integer(123)]
);
assert_eq!(
map.get("key2").unwrap(),
&vec![Value::Float(45.67), Value::Boolean(true)]
);
assert_eq!(remaining, "");
let input = "section1 key1: value1, value2 key2: value3, value4 section2";
let ((title, map), remaining) = parse_section(input, input).unwrap();
assert_eq!(title, "section1");
assert_eq!(map.len(), 2);
assert_eq!(remaining, "section2");
let input = "section1 key1: value1, value2";
let ((title, map), remaining) = parse_section(input, input).unwrap();
assert_eq!(title, "section1");
assert_eq!(map.len(), 1);
assert_eq!(
map.get("key1").unwrap(),
&vec![
Value::String("value1".to_string()),
Value::String("value2".to_string())
]
);
assert_eq!(remaining, "");
}
#[test]
fn test_parse_section_mixed_spacing() {
let input = "config key1:value1,value2 key2 : value3 , value4";
let ((title, map), remaining) = parse_section(input, input).unwrap();
assert_eq!(title, "config");
assert_eq!(map.len(), 2);
assert_eq!(
map.get("key1").unwrap(),
&vec![
Value::String("value1".to_string()),
Value::String("value2".to_string())
]
);
assert_eq!(
map.get("key2").unwrap(),
&vec![
Value::String("value3".to_string()),
Value::String("value4".to_string())
]
);
assert_eq!(remaining, "");
}
#[test]
fn test_parse_document_basic() {
let input = "section1 key1: value1, value2 key2: value3\nsection2 key3: value4, value5";
let doc = parse_document(input).unwrap();
println!("\n {:?} \n", doc);
assert_eq!(doc.len(), 2);
let section1 = &doc["section1"];
assert_eq!(section1.len(), 2);
assert_eq!(
section1.get("key1").unwrap().as_ref().unwrap(),
&vec![
Value::String("value1".to_string()),
Value::String("value2".to_string())
]
);
assert_eq!(
section1.get("key2").unwrap().as_ref().unwrap(),
&vec![Value::String("value3".to_string())]
);
let section2 = &doc["section2"];
assert_eq!(section2.len(), 1);
assert_eq!(
section2.get("key3").unwrap().as_ref().unwrap(),
&vec![
Value::String("value4".to_string()),
Value::String("value5".to_string())
]
);
}
#[test]
fn test_parse_document_with_mixed_types() {
let input = "section1 key1: value1, 123 key2: 45.67, true\nsection2 key3: false, 789";
let doc = parse_document(input).unwrap();
assert_eq!(doc.len(), 2);
let section1 = &doc["section1"];
assert_eq!(section1.len(), 2);
assert_eq!(
section1.get("key1").unwrap().as_ref().unwrap(),
&vec![Value::String("value1".to_string()), Value::Integer(123)]
);
assert_eq!(
section1.get("key2").unwrap().as_ref().unwrap(),
&vec![Value::Float(45.67), Value::Boolean(true)]
);
let section2 = &doc["section2"];
assert_eq!(section2.len(), 1);
assert_eq!(
section2.get("key3").unwrap().as_ref().unwrap(),
&vec![Value::Boolean(false), Value::Integer(789)]
);
}
#[test]
fn test_parse_document_with_multiple_line_breaks() {
let input = "section1 key1: value1, value2\n\n\nsection2 key2: value3, value4";
let doc = parse_document(input).unwrap();
assert_eq!(doc.len(), 2);
assert!(doc.contains_key("section1"));
assert!(doc.contains_key("section2"));
}
#[test]
fn test_parse_document_empty() {
let input = "";
assert_eq!(parse_document(input).is_err(), true);
}
#[test]
fn test_parse_document_with_template() {
let mut template = HashMap::new();
let mut section1_map = HashMap::new();
section1_map.insert("key1".to_string(), None);
section1_map.insert("key2".to_string(), None);
section1_map.insert("key3".to_string(), None);
let mut section2_map = HashMap::new();
section2_map.insert("key4".to_string(), None);
template.insert("section1".to_string(), section1_map);
template.insert("section2".to_string(), section2_map);
template.insert("section3".to_string(), HashMap::new());
let input = "section1 key1: value1, value2 key2: value3\nsection2 key4: value4, value5";
let result = parse_document_with_template(input, &template).unwrap();
assert_eq!(result.len(), 3);
assert!(result.contains_key("section1"));
assert!(result.contains_key("section2"));
assert!(result.contains_key("section3"));
let section1 = &result["section1"];
assert_eq!(section1.len(), 3);
assert_eq!(
section1.get("key1").unwrap().as_ref().unwrap(),
&vec![
Value::String("value1".to_string()),
Value::String("value2".to_string())
]
);
assert_eq!(
section1.get("key2").unwrap().as_ref().unwrap(),
&vec![Value::String("value3".to_string())]
);
assert!(section1.get("key3").unwrap().is_none());
let section2 = &result["section2"];
assert_eq!(section2.len(), 1);
assert_eq!(
section2.get("key4").unwrap().as_ref().unwrap(),
&vec![
Value::String("value4".to_string()),
Value::String("value5".to_string())
]
);
let section3 = &result["section3"];
assert_eq!(section3.len(), 0);
}
#[test]
fn test_parse_document_as_strings() {
let input = "section1 key1: value1, 123 key2: 45.67, true\nsection2 key3: false, 789";
let result = parse_document_as_strings(input, None);
assert!(result.is_ok());
}
#[test]
fn test_parse_complex_document() {
let input = "section1\n key1: value1, value2 key2: value3\nsection2\n key4: value4, value5";
let result = parse_document_as_strings(input, None).unwrap();
println!("{:?}", result);
}
#[test]
fn test_parse_this_sections_basic() {
let input = "section1 key1: value1, value2 key2: value3\nsection2 key3: value4, value5";
let titles = vec!["section1".to_string()];
let result = parse_this_sections(input, titles).unwrap();
assert_eq!(result.len(), 1);
assert!(result.contains_key("section1"));
assert!(!result.contains_key("section2"));
let section1 = &result["section1"];
assert_eq!(section1.len(), 2);
assert!(section1.get("key1").unwrap().is_some());
assert!(section1.get("key2").unwrap().is_some());
}
}
#[cfg(test)]
mod tests2 {
use crate::command_interpreter::task_parser::{
DocumentParser, parse_document_as_strings, parse_this_sections, parse_title,
};
use std::collections::HashMap;
use std::fs::File;
use std::io::{Read, Write};
use tempfile::tempdir;
#[test]
fn test_parse_document_as_strings_basic() {
let input =
"title1\n key1: value1, value2\n key2: value3 \ntitle2\n key3: value4, value5\n";
let result = parse_document_as_strings(input, None).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains_key("title1"));
assert!(result.contains_key("title2"));
let title1_map = &result["title1"];
assert_eq!(
title1_map.get("key1").unwrap().as_ref().unwrap(),
&vec!["value1".to_string(), "value2".to_string()]
);
assert_eq!(
title1_map.get("key2").unwrap().as_ref().unwrap(),
&vec!["value3".to_string()]
);
let title2_map = &result["title2"];
assert_eq!(
title2_map.get("key3").unwrap().as_ref().unwrap(),
&vec!["value4".to_string(), "value5".to_string()]
);
}
#[test]
fn test_parse_document_as_strings_mixed_types() {
let input = "title1\n key1: 1.0, 2.5\n key2: true, false \ntitle2\n key3: hello, world\n";
let result = parse_document_as_strings(input, None).unwrap();
assert_eq!(result.len(), 2);
let title1_map = &result["title1"];
assert_eq!(
title1_map.get("key1").unwrap().as_ref().unwrap(),
&vec!["1".to_string(), "2.5".to_string()]
); assert_eq!(
title1_map.get("key2").unwrap().as_ref().unwrap(),
&vec!["true".to_string(), "false".to_string()]
);
let title2_map = &result["title2"];
assert_eq!(
title2_map.get("key3").unwrap().as_ref().unwrap(),
&vec!["hello".to_string(), "world".to_string()]
);
}
#[test]
fn test_parse_document_as_strings_with_template() {
let input = "title1\n key1: 1, 2\n";
let mut template = HashMap::new();
let mut title1_map = HashMap::new();
title1_map.insert("key1".to_string(), None);
title1_map.insert("key2".to_string(), None);
template.insert("title1".to_string(), title1_map);
template.insert("title2".to_string(), HashMap::new());
let result = parse_document_as_strings(input, Some(template)).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains_key("title1"));
assert!(result.contains_key("title2"));
let title1_map = &result["title1"];
assert_eq!(
title1_map.get("key1").unwrap().as_ref().unwrap(),
&vec!["1".to_string(), "2".to_string()]
);
assert!(title1_map.get("key2").unwrap().is_none());
}
#[test]
fn test_parse_document_as_strings_empty() {
let input = "";
let res = parse_document_as_strings(input, None).is_err();
assert_eq!(res, true);
}
#[test]
fn test_parse_document_as_strings_malformed() {
let input = "title1\n key1: value1, \n invalid structure";
let result = parse_document_as_strings(input, None);
assert!(result.is_err());
}
#[test]
fn test_parse_document_from_file() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test_doc.txt");
let mut file = File::create(&file_path).unwrap();
writeln!(file, "config").unwrap();
writeln!(file, " port: 8080").unwrap();
writeln!(file, " hosts: localhost, 127.0.0.1").unwrap();
writeln!(file, "logging").unwrap();
writeln!(file, " level: debug, info, warn").unwrap();
writeln!(file, " enabled: true").unwrap();
let mut file = File::open(&file_path).unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
let result = parse_document_as_strings(&contents, None).unwrap();
assert_eq!(result.len(), 2);
let config = &result["config"];
assert_eq!(
config.get("port").unwrap().as_ref().unwrap(),
&vec!["8080".to_string()]
);
assert_eq!(
config.get("hosts").unwrap().as_ref().unwrap(),
&vec!["localhost".to_string(), "127.0.0.1".to_string()]
);
let logging = &result["logging"];
assert_eq!(
logging.get("level").unwrap().as_ref().unwrap(),
&vec!["debug".to_string(), "info".to_string(), "warn".to_string()]
);
assert_eq!(
logging.get("enabled").unwrap().as_ref().unwrap(),
&vec!["true".to_string()]
);
}
#[test]
fn test_from_r_string() {
let input = r#"system_config
memory: 8GB, 16GB, 32GB
cpu_cores: 4, 8, 16
ssd_capacity: 256GB, 512GB, 1TB
"#;
let result = parse_title(input, input);
assert!(result.is_ok());
let (title, _) = result.unwrap();
assert_eq!(title, "system_config");
}
#[test]
fn test_parse_document_complex() {
let input = r#"
system_config
memory: 8GB, 16GB, 32GB
cpu_cores: 4, 8, 16
ssd_capacity: 256GB, 512GB, 1TB
network_settings
ip_addresses: 192.168.1.1, 10.0.0.1
ports: 80, 443, 8080
protocols: http, https, tcp
user_preferences
theme: dark, light
font_size: 12, 14, 16
notifications: true, false
"#;
let result = parse_document_as_strings(input, None).unwrap();
assert_eq!(result.len(), 3);
let system = &result["system_config"];
assert_eq!(
system.get("memory").unwrap().as_ref().unwrap(),
&vec!["8GB".to_string(), "16GB".to_string(), "32GB".to_string()]
);
assert_eq!(
system.get("cpu_cores").unwrap().as_ref().unwrap(),
&vec!["4".to_string(), "8".to_string(), "16".to_string()]
);
assert_eq!(
system.get("ssd_capacity").unwrap().as_ref().unwrap(),
&vec!["256GB".to_string(), "512GB".to_string(), "1TB".to_string()]
);
let network = &result["network_settings"];
assert_eq!(
network.get("ip_addresses").unwrap().as_ref().unwrap(),
&vec!["192.168.1.1".to_string(), "10.0.0.1".to_string()]
);
assert_eq!(
network.get("ports").unwrap().as_ref().unwrap(),
&vec!["80".to_string(), "443".to_string(), "8080".to_string()]
);
assert_eq!(
network.get("protocols").unwrap().as_ref().unwrap(),
&vec!["http".to_string(), "https".to_string(), "tcp".to_string()]
);
let prefs = &result["user_preferences"];
assert_eq!(
prefs.get("theme").unwrap().as_ref().unwrap(),
&vec!["dark".to_string(), "light".to_string()]
);
assert_eq!(
prefs.get("font_size").unwrap().as_ref().unwrap(),
&vec!["12".to_string(), "14".to_string(), "16".to_string()]
);
assert_eq!(
prefs.get("notifications").unwrap().as_ref().unwrap(),
&vec!["true".to_string(), "false".to_string()]
);
}
#[test]
fn test_parse_document_with_special_characters() {
let input = r#"
database
connection_string: mongodb://user:pass@localhost:27017, postgresql://postgres:password@localhost:5432
timeout: 30s, 1m, 5m
paths
log_dir: /var/log/app, C:\logs\app
data_dir: /var/data, D:\data
"#;
let result = parse_document_as_strings(input, None).unwrap();
assert_eq!(result.len(), 2);
let db = &result["database"];
assert_eq!(
db.get("connection_string").unwrap().as_ref().unwrap(),
&vec![
"mongodb://user:pass@localhost:27017".to_string(),
"postgresql://postgres:password@localhost:5432".to_string()
]
);
let paths = &result["paths"];
assert_eq!(
paths.get("log_dir").unwrap().as_ref().unwrap(),
&vec!["/var/log/app".to_string(), "C:\\logs\\app".to_string()]
);
}
#[test]
fn test_parse_document_with_comments() {
let input = r#"
database
connection_string: mongodb://user:pass@localhost:27017, postgresql://postgres:password@localhost:5432
timeout: 30s, 1m, 5m
// this is comment
# this is comment
paths
log_dir: /var/log/app, C:\logs\app
data_dir: /var/data, D:\data
"#;
let result = parse_document_as_strings(input, None).unwrap();
assert_eq!(result.len(), 2);
let db = &result["database"];
assert_eq!(
db.get("connection_string").unwrap().as_ref().unwrap(),
&vec![
"mongodb://user:pass@localhost:27017".to_string(),
"postgresql://postgres:password@localhost:5432".to_string()
]
);
let paths = &result["paths"];
assert_eq!(
paths.get("log_dir").unwrap().as_ref().unwrap(),
&vec!["/var/log/app".to_string(), "C:\\logs\\app".to_string()]
);
}
#[test]
fn test_parse_this_sections_with_comments() {
let input = r#"
// Comment line
config
host: localhost
port: 8080
# Another comment
database
host: db.example.com
% Yet another comment
logging
level: debug
"#;
let titles = vec!["config".to_string(), "logging".to_string()];
let result = parse_this_sections(input, titles).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains_key("config"));
assert!(result.contains_key("logging"));
assert!(!result.contains_key("database"));
}
#[test]
fn test_document_parser_basic() {
let input = "config host: localhost port: 8080\nlogging level: debug";
let mut parser = DocumentParser::new(input.to_string());
let result = parser.parse_document().unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains_key("config"));
assert!(result.contains_key("logging"));
assert!(parser.is_success());
}
#[test]
fn test_document_parser_with_template() {
let input = "config host: localhost port: 8080";
let mut template = HashMap::new();
let mut config_section = HashMap::new();
config_section.insert("host".to_string(), None);
config_section.insert("port".to_string(), None);
template.insert("config".to_string(), config_section);
let mut parser = DocumentParser::new(input.to_string()).with_template(template);
println!("parser: {:?}", parser);
let result = parser.parse_document_as().unwrap();
let config = &result["config"];
assert_eq!(config.len(), 2);
assert!(config.get("host").unwrap().is_some());
assert!(config.get("port").unwrap().is_some());
}
#[test]
fn test_document_parser_as_strings() {
let input = "config port: 8080 debug: true";
let mut parser = DocumentParser::new(input.to_string());
let result = parser.parse_document_as_strings().unwrap();
assert_eq!(result.len(), 1);
assert!(result.contains_key("config"));
let config = &result["config"];
assert_eq!(
config.get("port").unwrap().as_ref().unwrap(),
&vec!["8080".to_string()]
);
assert_eq!(
config.get("debug").unwrap().as_ref().unwrap(),
&vec!["true".to_string()]
);
}
#[test]
fn test_document_parser_this_sections() {
let input = "config host: localhost\ndatabase port: 5432\nlogging level: debug";
let mut parser = DocumentParser::new(input.to_string());
let titles = vec!["config".to_string(), "logging".to_string()];
let result = parser.parse_this_sections(titles).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains_key("config"));
assert!(result.contains_key("logging"));
assert!(!result.contains_key("database"));
}
#[test]
fn test_document_parser_error_handling() {
let input = "invalid input";
let mut parser = DocumentParser::new(input.to_string());
let result = parser.parse_document();
assert!(result.is_err());
assert!(!parser.is_success());
assert!(parser.get_error().is_some());
}
#[test]
fn test_document_parser_reset() {
let input = "config host: localhost";
let mut parser = DocumentParser::new(input.to_string());
parser.parse_document().unwrap();
assert!(parser.is_success());
parser.reset();
assert!(!parser.is_success());
assert!(parser.get_result().is_none());
assert!(parser.get_error().is_none());
}
#[test]
fn test_document_parser_set_input() {
let mut parser = DocumentParser::new("invalid".to_string());
parser.parse_document().unwrap_err();
assert!(!parser.is_success());
parser.set_input("config host: localhost".to_string());
parser.parse_document().unwrap();
assert!(parser.is_success());
}
}
#[cfg(test)]
mod template_tests {
use crate::command_interpreter::task_parser::{Value, parse_document_as, parse_this_sections};
use std::collections::HashMap;
#[test]
fn test_parse_document_as_with_complex_template() {
let mut template = HashMap::new();
let mut section1 = HashMap::new();
section1.insert("existing_key".to_string(), None);
section1.insert("missing_key1".to_string(), None);
section1.insert("missing_key2".to_string(), None);
template.insert("config".to_string(), section1);
let mut section2 = HashMap::new();
section2.insert("db_host".to_string(), None);
section2.insert("db_port".to_string(), None);
section2.insert("db_name".to_string(), None);
template.insert("database".to_string(), section2);
let mut section3 = HashMap::new();
section3.insert("level".to_string(), None);
section3.insert("format".to_string(), None);
section3.insert("output".to_string(), None);
template.insert("logging".to_string(), section3);
let input = "config existing_key: value1, value2\nlogging level: debug, info";
let result = parse_document_as(input, Some(template)).unwrap();
assert_eq!(result.len(), 3);
assert!(result.contains_key("config"));
assert!(result.contains_key("database"));
assert!(result.contains_key("logging"));
let config = &result["config"];
assert_eq!(config.len(), 3);
assert!(config.get("existing_key").unwrap().is_some());
assert!(config.get("missing_key1").unwrap().is_none());
assert!(config.get("missing_key2").unwrap().is_none());
let database = &result["database"];
assert_eq!(database.len(), 3);
assert!(database.get("db_host").unwrap().is_none());
assert!(database.get("db_port").unwrap().is_none());
assert!(database.get("db_name").unwrap().is_none());
let logging = &result["logging"];
assert_eq!(logging.len(), 3);
assert!(logging.get("level").unwrap().is_some());
assert!(logging.get("format").unwrap().is_none());
assert!(logging.get("output").unwrap().is_none());
}
#[test]
fn test_parse_document_as_nested_template_structure() {
let mut template = HashMap::new();
let mut server_config = HashMap::new();
server_config.insert("host".to_string(), None);
server_config.insert("port".to_string(), None);
server_config.insert("ssl_enabled".to_string(), None);
server_config.insert("ssl_cert".to_string(), None);
server_config.insert("ssl_key".to_string(), None);
server_config.insert("max_connections".to_string(), None);
server_config.insert("timeout".to_string(), None);
template.insert("server".to_string(), server_config);
let mut auth_config = HashMap::new();
auth_config.insert("enabled".to_string(), None);
auth_config.insert("providers".to_string(), None);
auth_config.insert("oauth_client_id".to_string(), None);
auth_config.insert("oauth_secret".to_string(), None);
auth_config.insert("ldap_server".to_string(), None);
auth_config.insert("ldap_base_dn".to_string(), None);
template.insert("authentication".to_string(), auth_config);
let mut monitoring = HashMap::new();
monitoring.insert("metrics_enabled".to_string(), None);
monitoring.insert("health_check_interval".to_string(), None);
monitoring.insert("alert_endpoints".to_string(), None);
template.insert("monitoring".to_string(), monitoring);
let input = r#"
server
host: localhost, 0.0.0.0
port: 8080, 8443
ssl_enabled: true
authentication
enabled: true
providers: oauth, ldap
"#;
let result = parse_document_as(input, Some(template)).unwrap();
assert_eq!(result.len(), 3);
let server = &result["server"];
assert_eq!(server.len(), 7);
assert!(server.get("host").unwrap().is_some());
assert!(server.get("port").unwrap().is_some());
assert!(server.get("ssl_enabled").unwrap().is_some());
assert!(server.get("ssl_cert").unwrap().is_none());
assert!(server.get("ssl_key").unwrap().is_none());
assert!(server.get("max_connections").unwrap().is_none());
assert!(server.get("timeout").unwrap().is_none());
let auth = &result["authentication"];
assert_eq!(auth.len(), 6);
assert!(auth.get("enabled").unwrap().is_some());
assert!(auth.get("providers").unwrap().is_some());
assert!(auth.get("oauth_client_id").unwrap().is_none());
let monitoring = &result["monitoring"];
assert_eq!(monitoring.len(), 3);
assert!(monitoring.get("metrics_enabled").unwrap().is_none());
assert!(monitoring.get("health_check_interval").unwrap().is_none());
assert!(monitoring.get("alert_endpoints").unwrap().is_none());
}
#[test]
fn test_parse_document_as_empty_template_sections() {
let mut template = HashMap::new();
template.insert("empty_section1".to_string(), HashMap::new());
template.insert("empty_section2".to_string(), HashMap::new());
let mut populated_section = HashMap::new();
populated_section.insert("key1".to_string(), None);
template.insert("populated".to_string(), populated_section);
let input = "populated key1: value1";
let result = parse_document_as(input, Some(template)).unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result["empty_section1"].len(), 0);
assert_eq!(result["empty_section2"].len(), 0);
assert_eq!(result["populated"].len(), 1);
assert!(result["populated"]["key1"].is_some());
}
#[test]
fn test_parse_document_as_with_mixed_value_types_template() {
let mut template = HashMap::new();
let mut config = HashMap::new();
config.insert("numbers".to_string(), None);
config.insert("booleans".to_string(), None);
config.insert("strings".to_string(), None);
config.insert("missing_mixed".to_string(), None);
template.insert("mixed_types".to_string(), config);
let input = "mixed_types numbers: 1, 2.5, 42 booleans: true, false strings: hello, world";
let result = parse_document_as(input, Some(template)).unwrap();
let mixed = &result["mixed_types"];
assert_eq!(mixed.len(), 4);
let numbers = mixed.get("numbers").unwrap().as_ref().unwrap();
assert_eq!(numbers.len(), 3);
assert_eq!(numbers[0], Value::Integer(1));
assert_eq!(numbers[1], Value::Float(2.5));
assert_eq!(numbers[2], Value::Integer(42));
let booleans = mixed.get("booleans").unwrap().as_ref().unwrap();
assert_eq!(booleans[0], Value::Boolean(true));
assert_eq!(booleans[1], Value::Boolean(false));
assert!(mixed.get("missing_mixed").unwrap().is_none());
}
#[test]
fn test_parse_document_as_large_template() {
let mut template = HashMap::new();
let mut app = HashMap::new();
app.insert("name".to_string(), None);
app.insert("version".to_string(), None);
app.insert("debug".to_string(), None);
app.insert("environment".to_string(), None);
template.insert("application".to_string(), app);
let mut primary_db = HashMap::new();
primary_db.insert("host".to_string(), None);
primary_db.insert("port".to_string(), None);
primary_db.insert("database".to_string(), None);
primary_db.insert("username".to_string(), None);
primary_db.insert("password".to_string(), None);
primary_db.insert("pool_size".to_string(), None);
template.insert("primary_database".to_string(), primary_db);
let mut cache_db = HashMap::new();
cache_db.insert("redis_host".to_string(), None);
cache_db.insert("redis_port".to_string(), None);
cache_db.insert("redis_db".to_string(), None);
cache_db.insert("ttl".to_string(), None);
template.insert("cache".to_string(), cache_db);
let mut security = HashMap::new();
security.insert("jwt_secret".to_string(), None);
security.insert("jwt_expiry".to_string(), None);
security.insert("cors_origins".to_string(), None);
security.insert("rate_limit".to_string(), None);
template.insert("security".to_string(), security);
let mut external = HashMap::new();
external.insert("email_service".to_string(), None);
external.insert("sms_service".to_string(), None);
external.insert("payment_gateway".to_string(), None);
external.insert("analytics_key".to_string(), None);
template.insert("external_services".to_string(), external);
let input = r#"
application
name: MyApp
debug: true
primary_database
host: localhost
port: 5432
security
cors_origins: http://localhost:3000, https://myapp.com
"#;
let result = parse_document_as(input, Some(template)).unwrap();
assert_eq!(result.len(), 5);
assert!(result.contains_key("application"));
assert!(result.contains_key("primary_database"));
assert!(result.contains_key("cache"));
assert!(result.contains_key("security"));
assert!(result.contains_key("external_services"));
let app = &result["application"];
assert_eq!(app.len(), 4);
assert!(app.get("name").unwrap().is_some());
assert!(app.get("debug").unwrap().is_some());
assert!(app.get("version").unwrap().is_none());
assert!(app.get("environment").unwrap().is_none());
let cache = &result["cache"];
assert_eq!(cache.len(), 4);
assert!(cache.values().all(|v| v.is_none()));
let external = &result["external_services"];
assert_eq!(external.len(), 4);
assert!(external.values().all(|v| v.is_none()));
}
#[test]
fn test_parse_document_as_no_template() {
let input = "section1 key1: value1, value2 key2: value3\nsection2 key3: value4, value5";
let result = parse_document_as(input, None).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains_key("section1"));
assert!(result.contains_key("section2"));
let section1 = &result["section1"];
assert_eq!(section1.len(), 2);
assert!(section1.get("key1").unwrap().is_some());
assert!(section1.get("key2").unwrap().is_some());
}
#[test]
fn test_parse_this_sections() {
let input = r#"
config
host: localhost
port: 8080
database
host: db.example.com
port: 5432
logging
level: debug
output: file
"#;
let titles = vec!["config".to_string(), "logging".to_string()];
let result = parse_this_sections(input, titles).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains_key("config"));
assert!(result.contains_key("logging"));
assert!(!result.contains_key("database"));
let config = &result["config"];
assert_eq!(config.len(), 2);
assert!(config.get("host").unwrap().is_some());
assert!(config.get("port").unwrap().is_some());
let logging = &result["logging"];
assert_eq!(logging.len(), 2);
assert!(logging.get("level").unwrap().is_some());
assert!(logging.get("output").unwrap().is_some());
}
#[test]
fn test_parse_this_sections_missing_titles() {
let input = "config host: localhost port: 8080";
let titles = vec!["missing1".to_string(), "missing2".to_string()];
let result = parse_this_sections(input, titles).unwrap();
assert_eq!(result.len(), 0); }
#[test]
fn test_parse_this_sections_partial_match() {
let input = r#"
section1
key1: value1
section2
key2: value2
section3
key3: value3
"#;
let titles = vec![
"section1".to_string(),
"missing".to_string(),
"section3".to_string(),
];
let result = parse_this_sections(input, titles).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains_key("section1"));
assert!(result.contains_key("section3"));
assert!(!result.contains_key("section2"));
assert!(!result.contains_key("missing"));
}
}
#[cfg(test)]
mod pseudonym_tests {
use crate::command_interpreter::task_parser::{DocumentParser, invert_vec_map, parse_document};
use std::collections::HashMap;
#[test]
fn test_invert_vec_map() {
let mut input = HashMap::new();
input.insert(
"real_name1".to_string(),
vec!["alias1".to_string(), "alias2".to_string()],
);
input.insert("real_name2".to_string(), vec!["alias3".to_string()]);
let result = invert_vec_map(&input);
assert_eq!(result.len(), 3);
assert_eq!(result.get("alias1").unwrap(), "real_name1");
assert_eq!(result.get("alias2").unwrap(), "real_name1");
assert_eq!(result.get("alias3").unwrap(), "real_name2");
}
#[test]
fn test_header_pseudonyms() {
let input = "cfg host: localhost port: 8080\ndb_config host: db.example.com";
let mut parser = DocumentParser::new(input.to_string());
let mut header_pseudonyms = HashMap::new();
header_pseudonyms.insert(
"config".to_string(),
vec!["cfg".to_string(), "configuration".to_string()],
);
header_pseudonyms.insert(
"database".to_string(),
vec!["db_config".to_string(), "db".to_string()],
);
parser.with_pseudonims(Some(header_pseudonyms), None);
let result = parser.parse_document().unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains_key("config"));
assert!(result.contains_key("database"));
assert!(!result.contains_key("cfg"));
assert!(!result.contains_key("db_config"));
let config = &result["config"];
assert!(config.get("host").unwrap().is_some());
assert!(config.get("port").unwrap().is_some());
}
#[test]
fn test_field_name_pseudonyms() {
let input = "config hostname: localhost prt: 8080";
let mut parser = DocumentParser::new(input.to_string());
let mut field_pseudonyms = HashMap::new();
field_pseudonyms.insert(
"host".to_string(),
vec!["hostname".to_string(), "server".to_string()],
);
field_pseudonyms.insert("port".to_string(), vec!["prt".to_string(), "p".to_string()]);
parser.with_pseudonims(None, Some(field_pseudonyms));
let result = parser.parse_document().unwrap();
let config = &result["config"];
assert_eq!(config.len(), 2);
assert!(config.get("host").unwrap().is_some());
assert!(config.get("port").unwrap().is_some());
assert!(!config.contains_key("hostname"));
assert!(!config.contains_key("prt"));
}
#[test]
fn test_both_header_and_field_pseudonyms() {
let input = "cfg hostname: localhost prt: 8080\ndb_config srv: db.example.com";
let mut parser = DocumentParser::new(input.to_string());
let mut header_pseudonyms = HashMap::new();
header_pseudonyms.insert("config".to_string(), vec!["cfg".to_string()]);
header_pseudonyms.insert("database".to_string(), vec!["db_config".to_string()]);
let mut field_pseudonyms = HashMap::new();
field_pseudonyms.insert("host".to_string(), vec!["hostname".to_string()]);
field_pseudonyms.insert("port".to_string(), vec!["prt".to_string()]);
field_pseudonyms.insert("server".to_string(), vec!["srv".to_string()]);
parser.with_pseudonims(Some(header_pseudonyms), Some(field_pseudonyms));
let result = parser.parse_document().unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains_key("config"));
assert!(result.contains_key("database"));
let config = &result["config"];
assert!(config.get("host").unwrap().is_some());
assert!(config.get("port").unwrap().is_some());
let database = &result["database"];
assert!(database.get("server").unwrap().is_some());
}
#[test]
fn test_pseudonyms_with_template() {
let input = "cfg hostname: localhost port: 8080";
let mut parser = DocumentParser::new(input.to_string());
let mut template = HashMap::new();
let mut config_section = HashMap::new();
config_section.insert("host".to_string(), None);
config_section.insert("port".to_string(), None);
template.insert("config".to_string(), config_section);
parser = parser.with_template(template);
let mut header_pseudonyms = HashMap::new();
header_pseudonyms.insert("config".to_string(), vec!["cfg".to_string()]);
let mut field_pseudonyms = HashMap::new();
field_pseudonyms.insert("host".to_string(), vec!["hostname".to_string()]);
parser.with_pseudonims(Some(header_pseudonyms), Some(field_pseudonyms));
let result = parser.parse_document_as().unwrap();
print!("{:?}", result);
let config = &result["config"];
assert_eq!(config.len(), 2);
assert!(config.get("host").unwrap().is_some());
assert!(config.get("port").unwrap().is_some()); }
#[test]
fn test_pseudonyms_no_match() {
let input = "config host: localhost port: 8080";
let mut parser = DocumentParser::new(input.to_string());
let mut header_pseudonyms = HashMap::new();
header_pseudonyms.insert("database".to_string(), vec!["db".to_string()]);
let mut field_pseudonyms = HashMap::new();
field_pseudonyms.insert("server".to_string(), vec!["srv".to_string()]);
parser.with_pseudonims(Some(header_pseudonyms), Some(field_pseudonyms));
let result = parser.parse_document().unwrap();
assert!(result.contains_key("config"));
let config = &result["config"];
assert!(config.get("host").unwrap().is_some());
assert!(config.get("port").unwrap().is_some());
}
#[test]
fn test_pseudonyms_with_parse_this_sections() {
let input = "cfg hostname: localhost\ndb_config srv: db.example.com\nlogging level: debug";
let mut parser = DocumentParser::new(input.to_string());
let mut header_pseudonyms = HashMap::new();
header_pseudonyms.insert("config".to_string(), vec!["cfg".to_string()]);
header_pseudonyms.insert("database".to_string(), vec!["db_config".to_string()]);
let mut field_pseudonyms = HashMap::new();
field_pseudonyms.insert("host".to_string(), vec!["hostname".to_string()]);
field_pseudonyms.insert("server".to_string(), vec!["srv".to_string()]);
parser.with_pseudonims(Some(header_pseudonyms), Some(field_pseudonyms));
let titles = vec!["config".to_string(), "database".to_string()];
let result = parser.parse_this_sections(titles).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains_key("config"));
assert!(result.contains_key("database"));
assert!(!result.contains_key("logging"));
let config = &result["config"];
assert!(config.get("host").unwrap().is_some());
let database = &result["database"];
assert!(database.get("server").unwrap().is_some());
}
#[test]
fn test_find_actual_pseudonyms_conversion() {
let input = "cfg host: localhost\ndb_config port: 5432\nlogging level: debug";
let mut parser = DocumentParser::new(input.to_string());
let mut header_pseudonyms = HashMap::new();
header_pseudonyms.insert(
"config".to_string(),
vec!["cfg".to_string(), "configuration".to_string()],
);
header_pseudonyms.insert(
"database".to_string(),
vec!["db_config".to_string(), "db".to_string()],
);
parser.with_pseudonims(Some(header_pseudonyms), None);
let full_doc = parse_document(&input).unwrap();
let real_names = vec![
"config".to_string(),
"database".to_string(),
"unknown".to_string(),
];
let actual_pseudonyms = parser.find_actual_pseudonyms(&full_doc, real_names);
assert_eq!(actual_pseudonyms.len(), 3);
assert_eq!(actual_pseudonyms[0], "cfg"); assert_eq!(actual_pseudonyms[1], "db_config"); assert_eq!(actual_pseudonyms[2], "unknown"); }
}
#[cfg(test)]
mod error_handling_tests {
use crate::command_interpreter::task_parser::{
DocumentParser, parse_document, parse_key_value_pair, parse_section, parse_value,
};
use std::collections::HashMap;
#[test]
fn test_missing_colon_error() {
let input = "config key1 value1";
let result = parse_document(input);
assert!(result.is_err());
let mut parser = DocumentParser::new(input.to_string());
let result = parser.parse_document();
assert!(result.is_err());
assert!(parser.get_error().is_some());
assert!(
parser.get_error().unwrap().contains("colon")
|| parser.get_error().unwrap().contains("key")
);
}
#[test]
fn test_empty_key_error() {
let input = "config : value1";
let result = parse_document(input);
assert!(result.is_err());
}
#[test]
fn test_empty_value_error() {
let input = "config key1:";
let result = parse_key_value_pair(input, input);
assert!(result.is_err());
}
#[test]
fn test_multiple_colons_error() {
let input = "config key1: value1: extra";
let mut parser = DocumentParser::new(input.to_string());
let _validation_result = parser.validate_syntax();
let _parse_result = parser.parse_document();
}
#[test]
fn test_unmatched_brackets_error() {
let input = "config key1: [1.0, 2.0";
let parser = DocumentParser::new(input.to_string());
let validation_result = parser.validate_syntax();
assert!(validation_result.is_err());
assert!(validation_result.unwrap_err().contains("bracket"));
}
#[test]
fn test_unmatched_parentheses_error() {
let input = "config key1: Some(value";
let parser = DocumentParser::new(input.to_string());
let validation_result = parser.validate_syntax();
assert!(validation_result.is_err());
assert!(validation_result.unwrap_err().contains("parentheses"));
}
#[test]
fn test_invalid_vector_syntax() {
let input = "config key1: [1.0, 2.0)";
let result = parse_value(input, input);
println!("result {:?}", result);
match result {
Ok((value, _)) => {
println!("Parser bug: mixed brackets treated as string: {:?}", value);
assert!(value.as_string().is_some());
}
Err(e) => {
println!("Correct: mixed brackets detected as error: {}", e);
assert!(
format!("{}", e).contains("bracket") || format!("{}", e).contains("vector")
);
}
}
}
#[test]
fn test_invalid_optional_syntax() {
let input = "config key1: Some value";
let result = parse_value(input, input);
assert!(result.is_ok());
}
#[test]
fn test_malformed_vector_values() {
let input = "config key1: [1.0, abc, 3.0]";
let result = parse_value(input, input);
println!("result: {:?}", result);
match result {
Ok((value, _)) => {
println!("Parser bug: malformed vector treated as valid: {:?}", value);
}
Err(e) => {
println!("Correct: malformed vector detected as error: {}", e);
assert!(
format!("{}", e).contains("vector")
|| format!("{}", e).contains("float")
|| format!("{}", e).contains("abc")
);
}
}
}
#[test]
fn test_empty_section_name() {
let input = " key1: value1";
let result = parse_document(input);
assert!(result.is_err());
}
#[test]
fn test_section_name_with_spaces() {
let input = "config section key1: value1";
let result = parse_section(input, input);
if let Ok(((title, _), _)) = result {
assert_eq!(title, "config");
}
}
#[test]
fn test_duplicate_sections() {
let input = "config key1: value1\nconfig key2: value2";
let result = parse_document(input);
match result {
Ok(doc) => {
let config = &doc["config"];
println!("Duplicate sections merged: {:?}", config);
assert!(config.len() >= 1);
}
Err(e) => {
let error_msg = format!("{}", e);
println!("Duplicate sections error: {}", error_msg);
assert!(
error_msg.contains("Duplicate")
|| error_msg.contains("section")
|| error_msg.contains("config")
);
}
}
}
#[test]
fn test_duplicate_keys_in_section() {
let input = "config key1: value1 key1: value2";
let result = parse_document(input);
assert!(result.is_err());
if let Err(e) = result {
let error_msg = format!("{}", e);
assert!(error_msg.contains("Duplicate key") || error_msg.contains("key1"));
}
}
#[test]
fn test_invalid_boolean_values() {
let input = "config debug: yes";
let result = parse_document(input);
assert!(result.is_ok());
if let Ok(doc) = result {
let config = &doc["config"];
let debug_val = config.get("debug").unwrap().as_ref().unwrap();
assert!(debug_val[0].as_string().is_some());
}
}
#[test]
fn test_extra_characters_after_values() {
let input = "config key1: value1 extra_chars_here";
let result = parse_document(input);
match result {
Ok(doc) => {
let config = &doc["config"];
println!("Extra characters parsed as: {:?}", config);
assert!(config.len() >= 1);
}
Err(e) => {
let error_msg = format!("{}", e);
println!("Extra characters error: {}", error_msg);
assert!(
error_msg.contains("extra")
|| error_msg.contains("key")
|| error_msg.contains("value")
|| error_msg.contains("colon")
);
}
}
}
#[test]
fn test_incomplete_some_value() {
let input = "config key1: Some(";
let result = parse_value(input, input);
println!("result: {:?}", result);
match result {
Ok((value, _)) => {
println!(
"Parser bug: incomplete Some( treated as string: {:?}",
value
);
assert!(value.as_string().is_some());
}
Err(e) => {
println!("Correct: incomplete Some( detected as error: {}", e);
assert!(
format!("{}", e).contains("Some")
|| format!("{}", e).contains("parenthes")
|| format!("{}", e).contains("incomplete")
);
}
}
}
#[test]
fn test_nested_some_values() {
let input = "config key1: Some(Some(42))";
let result = parse_value(input, input);
assert!(result.is_ok());
}
#[test]
fn test_invalid_number_formats() {
let inputs = vec![
"config key1: 1.2.3", "config key1: 1e", "config key1: .123.", "config key1: 123abc", ];
for input in inputs {
let result = parse_document(input);
assert!(result.is_ok()); }
}
#[test]
fn test_template_validation_errors() {
let mut parser = DocumentParser::new("config key1: value1".to_string());
let empty_template = HashMap::new();
parser = parser.with_template(empty_template);
let validation_result = parser.validate_template();
assert!(validation_result.is_err());
assert!(validation_result.unwrap_err().contains("empty"));
}
#[test]
fn test_template_empty_section_name() {
let mut parser = DocumentParser::new("config key1: value1".to_string());
let mut template = HashMap::new();
let mut section = HashMap::new();
section.insert("key1".to_string(), None);
template.insert("".to_string(), section);
parser = parser.with_template(template);
let validation_result = parser.validate_template();
assert!(validation_result.is_err());
assert!(
validation_result
.unwrap_err()
.contains("empty section name")
);
}
#[test]
fn test_template_empty_field_name() {
let mut parser = DocumentParser::new("config key1: value1".to_string());
let mut template = HashMap::new();
let mut section = HashMap::new();
section.insert("".to_string(), None); template.insert("config".to_string(), section);
parser = parser.with_template(template);
let validation_result = parser.validate_template();
assert!(validation_result.is_err());
assert!(validation_result.unwrap_err().contains("empty field name"));
}
#[test]
fn test_template_missing_required_section() {
let input = "config key1: value1";
let mut parser = DocumentParser::new(input.to_string());
let mut template = HashMap::new();
let mut config_section = HashMap::new();
config_section.insert("key1".to_string(), None);
template.insert("config".to_string(), config_section);
let mut required_section = HashMap::new();
required_section.insert("required_key".to_string(), None);
template.insert("required_section".to_string(), required_section);
parser = parser.with_template(template);
let result = parser.parse_document_as();
assert!(result.is_err());
assert!(result.unwrap_err().contains("missing"));
}
#[test]
fn test_template_missing_required_field() {
let input = "config key1: value1";
let mut parser = DocumentParser::new(input.to_string());
let mut template = HashMap::new();
let mut config_section = HashMap::new();
config_section.insert("key1".to_string(), None);
config_section.insert("required_key".to_string(), None); template.insert("config".to_string(), config_section);
parser = parser.with_template(template);
let result = parser.parse_document_as();
assert!(result.is_err());
let error_msg = result.unwrap_err();
assert!(
error_msg.contains("Required field 'config.required_key' is missing from document.")
);
}
#[test]
fn test_syntax_validation_comprehensive() {
let mut parser = DocumentParser::new("".to_string());
let test_cases = vec![
("config key1 key2: value", "Multiple colons"),
("config key1: value1: extra", "Multiple colons"),
("config : value", "Empty key"),
("config key1:", "Empty value"),
("config key1: [1, 2", "Unmatched square brackets"),
("config key1: Some(value", "Unmatched parentheses"),
("config key1: ]1, 2[", "Unmatched square brackets"),
("config key1: )value(", "Unmatched parentheses"),
];
for (input, expected_error_type) in test_cases {
parser.set_input(input.to_string());
let validation_result = parser.validate_syntax();
if validation_result.is_err() {
let error_msg = validation_result.unwrap_err();
println!("Input: '{}' -> Error: '{}'", input, error_msg);
let error_lower = error_msg.to_lowercase();
match expected_error_type {
"Multiple colons" => assert!(error_lower.contains("colon")),
"Empty key" => {
assert!(error_lower.contains("empty") && error_lower.contains("key"))
}
"Empty value" => {
assert!(error_lower.contains("empty") && error_lower.contains("value"))
}
"Unmatched square brackets" => assert!(error_lower.contains("bracket")),
"Unmatched parentheses" => assert!(error_lower.contains("parentheses")),
_ => {}
}
}
}
}
#[test]
fn test_detailed_error_messages() {
let input = "invalid input without proper structure";
let mut parser = DocumentParser::new(input.to_string());
let result = parser.parse_document();
assert!(result.is_err());
let detailed_error = parser.get_detailed_error();
assert!(detailed_error.is_some());
let error_msg = detailed_error.unwrap();
assert!(error_msg.contains("Suggestions"));
assert!(error_msg.contains("colon"));
assert!(error_msg.contains("bracket"));
}
#[test]
fn test_error_recovery_and_reset() {
let mut parser = DocumentParser::new("invalid input".to_string());
let result = parser.parse_document();
assert!(result.is_err());
assert!(!parser.is_success());
assert!(parser.get_error().is_some());
parser.set_input("config key1: value1".to_string());
let result = parser.parse_document();
assert!(result.is_ok());
assert!(parser.is_success());
assert!(parser.get_error().is_none());
}
#[test]
fn test_comprehensive_validation() {
let mut parser = DocumentParser::new("config key1: [1, 2".to_string());
let validation_result = parser.validate_all();
assert!(validation_result.is_err());
parser.set_input("config key1: [1, 2]".to_string());
let validation_result = parser.validate_all();
assert!(validation_result.is_ok());
}
#[test]
fn test_common_typos_and_mistakes() {
let test_cases = vec![
("config key1 = value1", "Using = instead of :"),
("config key1; value1", "Using ; instead of :"),
("config key1 -> value1", "Using -> instead of :"),
("config\nkey1 value1", "Missing colon"),
(
"config\n key1 value1 value2",
"Missing comma between values",
),
("config key1: value1,", "Trailing comma"),
("config key1: ,value1", "Leading comma"),
("config key1: value1,, value2", "Double comma"),
("config key1: [value1, value2", "Unclosed bracket"),
("config key1: value1, value2]", "Unmatched closing bracket"),
("config key1: Some(value1", "Unclosed Some()"),
("config key1: Somevalue1)", "Missing opening parenthesis"),
];
for (input, description) in test_cases {
println!("Testing: {} - {}", description, input);
let mut parser = DocumentParser::new(input.to_string());
let syntax_result = parser.validate_syntax();
let parse_result = parser.parse_document();
if syntax_result.is_ok() && parse_result.is_ok() {
println!(" -> Parsed successfully (lenient parsing)");
} else {
if syntax_result.is_err() {
println!(
" -> Syntax validation caught: {}",
syntax_result.unwrap_err()
);
}
if parse_result.is_err() {
println!(" -> Parse error: {}", parse_result.unwrap_err());
}
}
}
}
#[test]
fn test_edge_case_inputs() {
let edge_cases = vec![
("", "Empty input"),
(" ", "Whitespace only"),
("\n\n\n", "Newlines only"),
("// comment only", "Comment only"),
("# comment only", "Hash comment only"),
("% comment only", "Percent comment only"),
("; comment only", "Semicolon comment only"),
("config", "Section name only"),
("config\n", "Section name with newline"),
("config key1:", "Key with colon but no value"),
("config key1: \n", "Key with colon and whitespace"),
(
"config key1: value1\n\n\n\nsection2",
"Multiple empty lines",
),
("config key1: value1 key2", "Missing colon for second key"),
];
for (input, description) in edge_cases {
println!(
"Testing edge case: {} - '{}'",
description,
input.replace('\n', "\\n")
);
let mut parser = DocumentParser::new(input.to_string());
let result = parser.parse_document();
match result {
Ok(_) => println!(" -> Parsed successfully"),
Err(e) => println!(" -> Error: {}", e),
}
}
}
#[test]
fn test_large_input_handling() {
let mut large_input = String::new();
for i in 0..1000 {
large_input.push_str(&format!("section{} key{}: value{}\n", i, i, i));
}
let mut parser = DocumentParser::new(large_input);
let start = std::time::Instant::now();
let result = parser.parse_document();
let duration = start.elapsed();
println!("Large input parsing took: {:?}", duration);
assert!(result.is_ok());
assert!(duration.as_secs() < 5); }
#[test]
fn test_unicode_and_special_characters() {
let unicode_cases = vec![
("config key1: café", "Unicode in value"),
("config clé: value1", "Unicode in key"),
("configuración key1: value1", "Unicode in section"),
("config key1: 🚀", "Emoji in value"),
("config key1: \"quoted value\"", "Quoted value"),
("config key1: 'single quoted'", "Single quoted value"),
("config key1: value with spaces", "Spaces in value"),
("config key_with_underscores: value", "Underscores in key"),
("config key-with-dashes: value", "Dashes in key"),
("config key.with.dots: value", "Dots in key"),
];
for (input, description) in unicode_cases {
println!("Testing Unicode case: {} - {}", description, input);
let mut parser = DocumentParser::new(input.to_string());
let result = parser.parse_document();
match result {
Ok(doc) => {
println!(" -> Parsed successfully: {:?}", doc);
}
Err(e) => {
println!(" -> Error: {}", e);
}
}
}
}
}