207 lines
5.1 KiB
Rust
207 lines
5.1 KiB
Rust
|
|
//! Tests for semantic validation integration
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use crate::lsp::document::Document;
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_reserved_keyword_caught_by_parser() {
|
||
|
|
// Reserved keywords are caught by the parser, not the validator
|
||
|
|
// This test verifies that parse errors catch reserved keywords
|
||
|
|
let source = r#"
|
||
|
|
character Alice {
|
||
|
|
self: "Bad field name"
|
||
|
|
}
|
||
|
|
"#;
|
||
|
|
let doc = Document::new(source.to_string());
|
||
|
|
|
||
|
|
// Should have parse error for reserved keyword
|
||
|
|
assert!(
|
||
|
|
!doc.parse_errors.is_empty(),
|
||
|
|
"Parser should catch reserved keyword 'self' as field name"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_valid_fields_no_validation_errors() {
|
||
|
|
let source = r#"
|
||
|
|
character Alice {
|
||
|
|
age: 7
|
||
|
|
name: "Alice"
|
||
|
|
}
|
||
|
|
"#;
|
||
|
|
let doc = Document::new(source.to_string());
|
||
|
|
|
||
|
|
// Should have no validation errors
|
||
|
|
assert!(
|
||
|
|
doc.resolve_errors.is_empty(),
|
||
|
|
"Valid code should have no validation errors"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_trait_range_validation() {
|
||
|
|
let source = r#"
|
||
|
|
character Alice {
|
||
|
|
bond: 1.5
|
||
|
|
}
|
||
|
|
"#;
|
||
|
|
let doc = Document::new(source.to_string());
|
||
|
|
|
||
|
|
// Should have error for bond value out of range
|
||
|
|
assert!(
|
||
|
|
!doc.resolve_errors.is_empty(),
|
||
|
|
"Should detect bond value out of range [0.0, 1.0]"
|
||
|
|
);
|
||
|
|
|
||
|
|
let error_message = format!("{}", doc.resolve_errors[0]);
|
||
|
|
assert!(
|
||
|
|
error_message.contains("1.5") || error_message.contains("range"),
|
||
|
|
"Error should mention the value or range: {}",
|
||
|
|
error_message
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_valid_trait_ranges() {
|
||
|
|
let source = r#"
|
||
|
|
character Alice {
|
||
|
|
bond: 0.75
|
||
|
|
trust: 0.0
|
||
|
|
love: 1.0
|
||
|
|
}
|
||
|
|
"#;
|
||
|
|
let doc = Document::new(source.to_string());
|
||
|
|
|
||
|
|
// Should have no validation errors for valid trait ranges
|
||
|
|
assert!(
|
||
|
|
doc.resolve_errors.is_empty(),
|
||
|
|
"Valid trait values should produce no errors"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_life_arc_transition_validation() {
|
||
|
|
let source = r#"
|
||
|
|
life_arc Growing {
|
||
|
|
state child {
|
||
|
|
on birthday -> adult
|
||
|
|
}
|
||
|
|
}
|
||
|
|
"#;
|
||
|
|
let doc = Document::new(source.to_string());
|
||
|
|
|
||
|
|
// Should have error for transition to unknown state 'adult'
|
||
|
|
assert!(
|
||
|
|
!doc.resolve_errors.is_empty(),
|
||
|
|
"Should detect transition to undefined state"
|
||
|
|
);
|
||
|
|
|
||
|
|
let error_message = format!("{}", doc.resolve_errors[0]);
|
||
|
|
assert!(
|
||
|
|
error_message.contains("adult") || error_message.contains("unknown"),
|
||
|
|
"Error should mention unknown state"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_valid_life_arc_transitions() {
|
||
|
|
let source = r#"
|
||
|
|
life_arc Growing {
|
||
|
|
state child {
|
||
|
|
on birthday -> teen
|
||
|
|
}
|
||
|
|
state teen {
|
||
|
|
on birthday -> adult
|
||
|
|
}
|
||
|
|
state adult {}
|
||
|
|
}
|
||
|
|
"#;
|
||
|
|
let doc = Document::new(source.to_string());
|
||
|
|
|
||
|
|
// Should have no validation errors
|
||
|
|
assert!(
|
||
|
|
doc.resolve_errors.is_empty(),
|
||
|
|
"Valid life arc should produce no errors"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_schedule_overlap_validation() {
|
||
|
|
let source = r#"
|
||
|
|
schedule Daily {
|
||
|
|
08:00 -> 10:00: morning {}
|
||
|
|
09:00 -> 11:00: overlap {}
|
||
|
|
}
|
||
|
|
"#;
|
||
|
|
let doc = Document::new(source.to_string());
|
||
|
|
|
||
|
|
// Should have error for overlapping schedule blocks
|
||
|
|
assert!(
|
||
|
|
!doc.resolve_errors.is_empty(),
|
||
|
|
"Should detect overlapping schedule blocks"
|
||
|
|
);
|
||
|
|
|
||
|
|
let error_message = format!("{}", doc.resolve_errors[0]);
|
||
|
|
assert!(
|
||
|
|
error_message.contains("overlap"),
|
||
|
|
"Error should mention overlap"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_valid_schedule_no_overlaps() {
|
||
|
|
let source = r#"
|
||
|
|
schedule Daily {
|
||
|
|
08:00 -> 10:00: morning {}
|
||
|
|
10:00 -> 12:00: midday {}
|
||
|
|
12:00 -> 14:00: afternoon {}
|
||
|
|
}
|
||
|
|
"#;
|
||
|
|
let doc = Document::new(source.to_string());
|
||
|
|
|
||
|
|
// Should have no validation errors
|
||
|
|
assert!(
|
||
|
|
doc.resolve_errors.is_empty(),
|
||
|
|
"Non-overlapping schedule should produce no errors"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_multiple_validation_errors() {
|
||
|
|
// Test multiple validation errors at once
|
||
|
|
let source = r#"
|
||
|
|
character Alice {
|
||
|
|
bond: 2.0
|
||
|
|
trust: -0.5
|
||
|
|
}
|
||
|
|
"#;
|
||
|
|
let doc = Document::new(source.to_string());
|
||
|
|
|
||
|
|
// Should have multiple errors for out-of-range values
|
||
|
|
assert!(
|
||
|
|
doc.resolve_errors.len() >= 2,
|
||
|
|
"Should detect multiple range errors. Got {} errors",
|
||
|
|
doc.resolve_errors.len()
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_parse_and_validation_errors_separate() {
|
||
|
|
let source = r#"
|
||
|
|
character Alice {
|
||
|
|
character: "Reserved"
|
||
|
|
invalid syntax here
|
||
|
|
}
|
||
|
|
"#;
|
||
|
|
let doc = Document::new(source.to_string());
|
||
|
|
|
||
|
|
// Parse should fail, so we won't have validation errors
|
||
|
|
// (validation only runs on successfully parsed AST)
|
||
|
|
assert!(
|
||
|
|
!doc.parse_errors.is_empty() || !doc.resolve_errors.is_empty(),
|
||
|
|
"Should have either parse or validation errors"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|