Files
Marcos Paulo 15d7f5e873 feat: editor blocks and lint, vault knowledge, calendar plugin, macOS menu
- notescraft-lint (rules, safe fixes, learned style) and notescraft-blocks (block model and edits); engine lint.* and block.* commands with CLI and MCP parity
- Lint tab, underlines, slash menu with preview in Source and Visual, block grip and menu, Settings > Editor and lint, Vim block keys
- Visual view: tables, note embeds, %% comments, wrapping property chips
- Vault knowledge (Related tab, assistant sources, Settings) and calendar plugin
- Sidebar top padding fixed, dot grid follows PaperCraft spacing, right-card tabs no longer cover the collapse button

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 12:36:00 -03:00

75 lines
2.4 KiB
Rust

//! Hostile input: nothing panics, caps error or flag truncation.
use notescraft_knowledge::caps::{MAX_NOTE_BYTES, MAX_PASSAGE_CHARS, MAX_PASSAGES_PER_NOTE};
use notescraft_knowledge::tokens::query_terms;
use notescraft_knowledge::{Error, Index, chunk, slice};
#[test]
fn huge_note_is_an_error_not_a_crash() {
let big = "word ".repeat(MAX_NOTE_BYTES / 4);
assert!(matches!(chunk(&big), Err(Error::NoteTooLarge { .. })));
let mut i = Index::default();
assert!(i.update("big.md", &big).is_err());
assert_eq!(i.note_count(), 0);
}
#[test]
fn big_but_allowed_note_is_truncated_with_flag() {
let text = "lorem ipsum dolor\n\n".repeat(MAX_NOTE_BYTES / 19);
let c = chunk(&text).unwrap();
assert!(c.truncated);
assert_eq!(c.passages.len(), MAX_PASSAGES_PER_NOTE);
}
#[test]
fn single_unbreakable_line_splits() {
let text = "x".repeat(MAX_NOTE_BYTES);
let c = chunk(&text).unwrap();
assert!(c.truncated);
assert!(c.passages.iter().all(|p| p.end - p.start <= MAX_PASSAGE_CHARS));
}
#[test]
fn binary_ish_text() {
let junk: String = (0u32..20_000).map(|n| char::from_u32(n % 0x300).unwrap_or('\0')).collect();
let mut i = Index::default();
let _ = i.update("bin.md", &junk);
let _ = i.update("nul.md", &"\0\u{1}\u{7f}\r\r\n---\n```\n".repeat(500));
assert!(i.search(&query_terms(&junk), 5).is_ok());
}
#[test]
fn hundred_thousand_headings() {
let text = "# h\n".repeat(100_000);
let c = chunk(&text).unwrap();
assert!(c.truncated);
let deep = "###### a\n".repeat(50_000) + &"# b\n".repeat(50_000);
assert!(chunk(&deep).is_ok());
}
#[test]
fn unclosed_fence_and_frontmatter() {
assert!(chunk(&format!("---\n{}", "k: v\n".repeat(10_000))).is_ok());
assert!(chunk(&format!("```\n{}", "code\n\n".repeat(10_000))).is_ok());
}
#[test]
fn invalid_ranges_and_emoji_boundaries() {
let s = "a😀b";
assert!(matches!(slice(s, 3, 1), Err(Error::InvalidRange { .. })));
assert!(matches!(slice(s, 0, usize::MAX), Err(Error::InvalidRange { .. })));
assert_eq!(slice(s, 2, 3), Err(Error::NotCharBoundary));
let text = "😀".repeat(5_000);
for p in chunk(&text).unwrap().passages {
assert!(slice(&text, p.start, p.end).is_ok());
}
}
#[test]
fn giant_query_is_capped() {
let q = "w ".repeat(1_000_000);
let mut i = Index::default();
i.update("a.md", "ww").unwrap();
assert!(i.search(&query_terms(&q), 5).is_ok());
}