Menu
Public repository / 9f0fd7a16e2dff7518389ff0ddbd603ebc6264ed
AkurAI-Notes / crates/notes-mcp/tests/mcp_test.rs
Latest change 21fd83f4a6eb769a0ee34aa30f721b5cdc6881af - Fix IDP OAuth: split public issuer vs internal loopback URL for token exchange by Claude Code
use akurai_storage::BTree;
use std::sync::{Arc, Mutex};
fn make_registry() -> notes_mcp::tools::ToolRegistry {
let dir = std::env::temp_dir().join(format!("notes-mcp-test-{}", std::process::id()));
let _ = std::fs::create_dir_all(&dir);
let unique = format!(
"test-{}.db",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
);
let db_path = dir.join(unique);
let db = BTree::open(&db_path).expect("create test db");
let owner_sub = "testuser@example.com".to_string();
notes_mcp::tools::ToolRegistry::new(Arc::new(Mutex::new(db)), None, None, owner_sub)
}
fn create_note_via_tool(
registry: ¬es_mcp::tools::ToolRegistry,
title: &str,
body: &str,
owner: &str,
) {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as i64;
let next_id = {
let mut db = registry.db.lock().unwrap();
let next = db
.get(b"_meta:next_note_id")
.ok()
.flatten()
.and_then(|b| String::from_utf8_lossy(&b).parse::<i64>().ok())
.unwrap_or(1);
db.insert(b"_meta:next_note_id", format!("{}", next + 1).as_bytes())
.unwrap();
db.commit().unwrap();
next
};
let note = akurai_json::Value::Object(vec![
("id".into(), akurai_json::Value::Int(next_id)),
("title".into(), akurai_json::Value::Str(title.into())),
("body".into(), akurai_json::Value::Str(body.into())),
("ownerSub".into(), akurai_json::Value::Str(owner.into())),
("createdAt".into(), akurai_json::Value::Int(now)),
("updatedAt".into(), akurai_json::Value::Int(now)),
("archived".into(), akurai_json::Value::Bool(false)),
]);
let mut key = b"notes:".to_vec();
key.extend_from_slice(&(next_id as u64).to_be_bytes());
let mut db = registry.db.lock().unwrap();
db.insert(&key, note.to_json().as_bytes()).unwrap();
db.commit().unwrap();
}
#[test]
fn initialize_returns_server_info() {
let registry = make_registry();
let req = r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}"#;
let resp = registry.handle_request(req);
assert!(resp.contains("\"jsonrpc\":\"2.0\""));
assert!(resp.contains("\"protocolVersion\":\"2024-11-05\""));
assert!(resp.contains("\"akurai-notes-mcp\""));
}
#[test]
fn tools_list_returns_all_notes_tools() {
let registry = make_registry();
let req = r#"{"jsonrpc":"2.0","id":2,"method":"tools/list"}"#;
let resp = registry.handle_request(req);
assert!(resp.contains("\"search_notes\""));
assert!(resp.contains("\"semantic_search\""));
assert!(resp.contains("\"get_note\""));
assert!(resp.contains("\"list_notebooks\""));
assert!(resp.contains("\"create_note\""));
assert!(resp.contains("\"update_note\""));
assert!(resp.contains("\"delete_note\""));
assert!(resp.contains("\"add_tag\""));
assert!(resp.contains("\"remove_tag\""));
}
#[test]
fn bad_json_returns_parse_error() {
let registry = make_registry();
let resp = registry.handle_request("not json");
assert!(resp.contains("\"code\":-32700"));
}
#[test]
fn search_notes_returns_owned_only() {
let registry = make_registry();
create_note_via_tool(
®istry,
"Meeting notes",
"Discussed Q3 roadmap",
"testuser@example.com",
);
create_note_via_tool(®istry, "Other note", "Not relevant", "other@example.com");
let req = r#"{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search_notes","arguments":{"query":"meeting"}}}"#;
let resp = registry.handle_request(req);
assert!(resp.contains("Meeting notes"));
}
#[test]
fn get_note_returns_full_note() {
let registry = make_registry();
create_note_via_tool(
®istry,
"Test Note",
"Hello world",
"testuser@example.com",
);
let req = r#"{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"get_note","arguments":{"id":1}}}"#;
let resp = registry.handle_request(req);
assert!(resp.contains("Test Note"));
assert!(resp.contains("Hello world"));
}
#[test]
fn create_note_inserts_and_returns_id() {
let registry = make_registry();
let req = r#"{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"create_note","arguments":{"title":"New Note","body":"Some body"}}}"#;
let resp = registry.handle_request(req);
assert!(resp.contains("\"result\""));
let get_req = r#"{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"get_note","arguments":{"id":1}}}"#;
let get_resp = registry.handle_request(get_req);
assert!(get_resp.contains("New Note"));
}
#[test]
fn unknown_tool_returns_error() {
let registry = make_registry();
let req = r#"{"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"nonexistent","arguments":{}}}"#;
let resp = registry.handle_request(req);
assert!(resp.contains("\"code\":-32603"));
}
#[test]
fn unknown_method_returns_error() {
let registry = make_registry();
let req = r#"{"jsonrpc":"2.0","id":8,"method":"bogus"}"#;
let resp = registry.handle_request(req);
assert!(resp.contains("\"code\":-32601"));
}