Menu
Public repository / 2c509452a4982321959914cab8d52b881e8368de
AkurAI-PassVault / crates/passvault-mcp/tests/notification_test.rs
Latest change 705fd90a65de57e8fe9074f8c225719cf6556674 - Checkpoint WIP + rustfmt + AkurAI Build CI/CD pipeline by Ólafur Búi Ólafsson
//! Regression: JSON-RPC notifications (messages with no `id`) must produce NO response
//! body, so the HTTP layer can answer with `202 Accepted` + empty body. Returning a
//! JSON-RPC body to a notification breaks strict MCP clients (codex/rmcp), which fail
//! with "Transport channel closed, when send initialized notification".
use std::sync::{Arc, Mutex};
use akurai_storage::BTree;
use passvault_core::crypto::Crypto;
use passvault_mcp::tools::ToolRegistry;
fn registry(tag: &str) -> ToolRegistry {
let db_path = format!("{}/passvault_notif_{tag}.db", env!("CARGO_TARGET_TMPDIR"));
let _ = std::fs::remove_file(&db_path);
let db = BTree::open(&db_path).expect("open temp db");
let crypto = Crypto::from_hex_key(&"ab".repeat(32)).expect("crypto key");
ToolRegistry::new(Arc::new(Mutex::new(db)), Arc::new(crypto))
}
#[test]
fn notification_initialized_has_no_response_body() {
let out = registry("init")
.handle_request(r#"{"jsonrpc":"2.0","method":"notifications/initialized"}"#);
assert!(
out.is_empty(),
"notification must yield empty body, got: {out}"
);
}
#[test]
fn message_without_id_has_no_response_body() {
let out = registry("noid").handle_request(r#"{"jsonrpc":"2.0","method":"tools/list"}"#);
assert!(
out.is_empty(),
"id-less message is a notification, got: {out}"
);
}
#[test]
fn request_with_id_still_returns_json() {
let out = registry("req")
.handle_request(r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}"#);
assert!(
out.contains("\"id\":1"),
"request must get a response with id, got: {out}"
);
assert!(
out.contains("serverInfo"),
"initialize must return serverInfo, got: {out}"
);
}