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>
76 lines
3.5 KiB
Rust
76 lines
3.5 KiB
Rust
//! The lint tools over MCP: `lint_check` and `lint_fix_all_safe` equal the engine, the rest is reachable by name,
|
|
//! and a read-only server answers reads only.
|
|
|
|
mod common;
|
|
|
|
use common::{ask, call_tool, is_error, registry, seed, temp_vault};
|
|
use notescraft_engine::Session;
|
|
use notescraft_mcp::{Server, ServerOptions};
|
|
use serde_json::{Value, json};
|
|
|
|
const MESSY: &str = "# A\n\n### B\n\ntext with gaps\n";
|
|
|
|
const WRITABLE: ServerOptions = ServerOptions { write: true, allow_delete: false, via_control: false };
|
|
|
|
fn fixture(name: &str, options: ServerOptions) -> (common::TempVault, Server) {
|
|
let vault = temp_vault(name);
|
|
seed(vault.path(), &[("messy.md", MESSY)]);
|
|
let session = Session::open(vault.path()).expect("open");
|
|
(vault, Server::new(session, registry(), options))
|
|
}
|
|
|
|
fn names(server: &mut Server) -> Vec<String> {
|
|
let reply = ask(server, &json!({"jsonrpc": "2.0", "id": 1, "method": "tools/list"}).to_string());
|
|
reply["result"]["tools"].as_array().unwrap().iter().map(|t| t["name"].as_str().unwrap().to_owned()).collect()
|
|
}
|
|
|
|
/// The engine's own answer over a twin vault (the server holds the first one's lock).
|
|
fn engine(id: &str, params: &Value) -> Value {
|
|
let twin = temp_vault("lint-mcp-twin");
|
|
seed(twin.path(), &[("messy.md", MESSY)]);
|
|
let mut session = Session::open(twin.path()).expect("session");
|
|
registry().run(id, &mut session, params).expect("engine run")
|
|
}
|
|
|
|
#[test]
|
|
fn lint_check_and_fix_all_safe_equal_the_engine() {
|
|
let (vault, mut server) = fixture("lint-mcp-parity", WRITABLE);
|
|
let params = json!({ "path": "messy.md" });
|
|
let checked = call_tool(&mut server, "lint_check", ¶ms);
|
|
assert!(checked.get("error").is_none(), "{checked}");
|
|
assert_eq!(checked, engine("lint.check", ¶ms));
|
|
assert_eq!(checked, call_tool(&mut server, "run_command", &json!({ "id": "lint.check", "params": params })));
|
|
let fixed = call_tool(&mut server, "lint_fix_all_safe", ¶ms);
|
|
assert_eq!(fixed, engine("lint.fix_all_safe", ¶ms));
|
|
assert_eq!(std::fs::read_to_string(vault.path().join("messy.md")).unwrap(), "# A\n\n## B\n\ntext with gaps\n");
|
|
}
|
|
|
|
#[test]
|
|
fn the_assistant_list_holds_only_the_two_lint_aliases() {
|
|
let (_vault, mut server) = fixture("lint-mcp-list", ServerOptions { via_control: true, ..WRITABLE });
|
|
let listed = names(&mut server);
|
|
for tool in ["lint_check", "lint_fix_all_safe"] {
|
|
assert!(listed.iter().any(|name| name == tool), "{tool}: {listed:?}");
|
|
}
|
|
assert!(!listed.iter().any(|name| name.starts_with("lint_") && !["lint_check", "lint_fix_all_safe"].contains(&name.as_str())), "{listed:?}");
|
|
}
|
|
|
|
#[test]
|
|
fn a_plain_server_lists_every_lint_command() {
|
|
let (_vault, mut server) = fixture("lint-mcp-plain", WRITABLE);
|
|
let listed = names(&mut server);
|
|
for tool in ["lint_check", "lint_fix", "lint_fix_all_safe", "lint_rules", "lint_dismiss", "lint_state", "lint_state_clear", "lint_style"] {
|
|
assert!(listed.iter().any(|name| name == tool), "{tool}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn a_read_only_server_checks_but_never_fixes() {
|
|
let (_vault, mut server) = fixture("lint-mcp-ro", ServerOptions { write: false, ..WRITABLE });
|
|
assert!(!is_error(&mut server, "lint_check", &json!({ "path": "messy.md" })));
|
|
assert!(!is_error(&mut server, "lint_rules", &json!({})));
|
|
assert!(is_error(&mut server, "lint_fix_all_safe", &json!({ "path": "messy.md" })));
|
|
assert!(is_error(&mut server, "lint_check", &json!({ "path": "../x.md" })));
|
|
assert!(is_error(&mut server, "lint_check", &json!({})), "`path` is required");
|
|
}
|