15d7f5e873
- 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>
75 lines
2.4 KiB
Rust
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());
|
|
}
|