Files
storybook/src/lsp/validation_tests.rs
Sienna Meridian Satterwhite 16deb5d237 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
2026-02-13 21:52:03 +00:00

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"
);
}
}