Files
storybook/src/lsp/document_edge_tests.rs
Sienna Meridian Satterwhite 25d59d6107 feat(type-system): implement concept_comparison with pattern matching
Added complete support for the new type system syntax including:

- concept: Base type declarations
- sub_concept: Enum and record sub-type definitions
- concept_comparison: Compile-time pattern matching with conditional guards

Parser changes:
- Added VariantPattern, FieldCondition, and Condition AST nodes
- Implemented "is" keyword for pattern matching (e.g., "CupType is Glass or CupType is Plastic")
- Added Value::Any variant to support universal type matching
- Disambiguated enum-like vs record-like sub_concept syntax

LSP updates:
- Added Value::Any match arms across code_actions, completion, hover, inlay_hints, and semantic_tokens
- Type inference and formatting support for Any values

Example fixes:
- Fixed syntax error in baker-family behaviors (missing closing brace in nested if)
- Removed deprecated core_enums.sb file
2026-02-14 09:28:20 +00:00

190 lines
5.4 KiB
Rust

//! Edge case tests for document functionality
#[cfg(test)]
mod tests {
use crate::lsp::document::Document;
#[test]
fn test_word_at_offset_unicode() {
let doc = Document::new("character Café { age: 7 }".to_string());
// Test finding "Café"
let word = doc.word_at_offset(10);
assert_eq!(word, Some("Café".to_string()));
}
#[test]
fn test_word_at_offset_underscore() {
let doc = Document::new("character snake_case { }".to_string());
let word = doc.word_at_offset(12);
assert_eq!(word, Some("snake_case".to_string()));
}
#[test]
fn test_word_at_offset_at_start() {
let doc = Document::new("character Alice { }".to_string());
let word = doc.word_at_offset(0);
assert_eq!(word, Some("character".to_string()));
}
#[test]
fn test_word_at_offset_at_end() {
let doc = Document::new("character Alice".to_string());
let word = doc.word_at_offset(14);
assert_eq!(word, Some("Alice".to_string()));
}
#[test]
fn test_word_at_offset_out_of_bounds() {
let doc = Document::new("test".to_string());
let word = doc.word_at_offset(1000);
assert_eq!(word, None);
}
#[test]
fn test_word_at_offset_on_whitespace() {
let doc = Document::new("character Alice".to_string());
let word = doc.word_at_offset(9); // Space between character and Alice
assert_eq!(word, None);
}
#[test]
fn test_word_at_offset_on_punctuation() {
let doc = Document::new("character Alice { }".to_string());
let word = doc.word_at_offset(16); // On '{'
assert_eq!(word, None);
}
#[test]
fn test_update_clears_old_symbols() {
let mut doc = Document::new("character Alice {}".to_string());
assert!(doc.name_table.resolve_name("Alice").is_some());
doc.update("character Bob {}".to_string());
assert!(doc.name_table.resolve_name("Alice").is_none());
assert!(doc.name_table.resolve_name("Bob").is_some());
}
#[test]
fn test_update_with_invalid_syntax() {
let mut doc = Document::new("character Alice {}".to_string());
assert!(doc.ast.is_some());
assert!(doc.parse_errors.is_empty());
doc.update("invalid { }".to_string());
assert!(doc.ast.is_none());
assert!(!doc.parse_errors.is_empty());
}
#[test]
fn test_empty_document_has_no_symbols() {
let doc = Document::new("".to_string());
assert_eq!(doc.name_table.all_entries().count(), 0);
}
#[test]
fn test_symbol_table_with_duplicates() {
let source = r#"
character Alice { age: 7 }
character Alice { age: 8 }
"#;
let doc = Document::new(source.to_string());
// Duplicate declarations should be caught during resolution
// NameTable from_file will fail, so we'll have an empty table and
// resolve_errors
assert!(
!doc.resolve_errors.is_empty(),
"Should have validation error for duplicate"
);
}
#[test]
fn test_mixed_declaration_types() {
let source = r#"
species Human {}
character Alice: Human {}
template Child {}
location Home {}
relationship Friends { Alice as friend {} Bob as friend {} }
"#;
let doc = Document::new(source.to_string());
assert!(doc.name_table.resolve_name("Human").is_some());
assert!(doc.name_table.resolve_name("Alice").is_some());
assert!(doc.name_table.resolve_name("Child").is_some());
assert!(doc.name_table.resolve_name("Home").is_some());
assert!(doc.name_table.resolve_name("Friends").is_some());
}
#[test]
fn test_life_arc_symbol_extraction() {
let source = r#"
life_arc Growing {
state child {}
state teen {}
state adult {}
}
"#;
let doc = Document::new(source.to_string());
assert!(doc.name_table.resolve_name("Growing").is_some());
let growing = doc.name_table.resolve_name("Growing").unwrap();
assert_eq!(growing.kind, crate::resolve::names::DeclKind::LifeArc);
}
#[test]
fn test_schedule_symbol_extraction() {
let source = r#"
schedule Daily {
08:00 -> 09:00: breakfast {}
09:00 -> 12:00: work {}
}
"#;
let doc = Document::new(source.to_string());
assert!(doc.name_table.resolve_name("Daily").is_some());
let daily = doc.name_table.resolve_name("Daily").unwrap();
assert_eq!(daily.kind, crate::resolve::names::DeclKind::Schedule);
}
#[test]
fn test_institution_symbol_extraction() {
let source = "institution School { type: education }";
let doc = Document::new(source.to_string());
assert!(doc.name_table.resolve_name("School").is_some());
let school = doc.name_table.resolve_name("School").unwrap();
assert_eq!(school.kind, crate::resolve::names::DeclKind::Institution);
}
#[test]
fn test_very_long_identifier() {
let long_name = "a".repeat(1000);
let source = format!("character {} {{}}", long_name);
let doc = Document::new(source);
assert!(doc.name_table.resolve_name(&long_name).is_some());
}
#[test]
fn test_multiline_document() {
let source = "\n\n\n\ncharacter Alice {\n\n\n age: 7\n\n\n}";
let doc = Document::new(source.to_string());
assert!(doc.ast.is_some());
assert!(doc.name_table.resolve_name("Alice").is_some());
}
}