release: Storybook v0.2.0 - Major syntax and features update
BREAKING CHANGES: - Relationship syntax now requires blocks for all participants - Removed self/other perspective blocks from relationships - Replaced 'guard' keyword with 'if' for behavior tree decorators Language Features: - Add tree-sitter grammar with improved if/condition disambiguation - Add comprehensive tutorial and reference documentation - Add SBIR v0.2.0 binary format specification - Add resource linking system for behaviors and schedules - Add year-long schedule patterns (day, season, recurrence) - Add behavior tree enhancements (named nodes, decorators) Documentation: - Complete tutorial series (9 chapters) with baker family examples - Complete reference documentation for all language features - SBIR v0.2.0 specification with binary format details - Added locations and institutions documentation Examples: - Convert all examples to baker family scenario - Add comprehensive working examples Tooling: - Zed extension with LSP integration - Tree-sitter grammar for syntax highlighting - Build scripts and development tools Version Updates: - Main package: 0.1.0 → 0.2.0 - Tree-sitter grammar: 0.1.0 → 0.2.0 - Zed extension: 0.1.0 → 0.2.0 - Storybook editor: 0.1.0 → 0.2.0
This commit is contained in:
206
src/lsp/validation_tests.rs
Normal file
206
src/lsp/validation_tests.rs
Normal file
@@ -0,0 +1,206 @@
|
||||
//! 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"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user