From 92aaa15ff8958f85d5e2e1a966a76f3705a269d1 Mon Sep 17 00:00:00 2001 From: Mohammad Ariful ISLAM Date: Thu, 23 Jul 2026 00:19:06 +0600 Subject: [PATCH] fix ci dependency specs and rust git api --- package.json | 6 +-- src-tauri/Cargo.lock | 2 +- src-tauri/src/commands/voice.rs | 2 +- src-tauri/src/git/mod.rs | 13 +++--- src-tauri/src/mcp/mod.rs | 2 +- src-tauri/src/mcp/transport.rs | 40 +++++++++++++------ src-tauri/tests/README.md | 4 +- src-tauri/tests/integration/test_config.rs | 2 +- .../tests/integration/test_vector_store.rs | 20 ++++++---- 9 files changed, 58 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index e790cbc..38d861e 100644 --- a/package.json +++ b/package.json @@ -20,14 +20,14 @@ "@milkdown/plugin-cursor": "^7.21.2", "@milkdown/plugin-history": "^7.21.2", "@milkdown/plugin-listener": "^7.21.2", - "@milkdown/plugin-math": "^7.21.2", - "@milkdown/plugin-table": "^7.21.2", + "@milkdown/plugin-math": "^7.5.9", + "@milkdown/plugin-table": "^5.3.1", "@milkdown/plugin-tooltip": "^7.21.2", "@milkdown/preset-commonmark": "^7.21.2", "@milkdown/preset-gfm": "^7.21.2", "@milkdown/prose": "^7.21.2", "@milkdown/utils": "^7.21.2", - "@tauri-apps/api": "^2.11.4", + "@tauri-apps/api": "^2.11.1", "@tauri-apps/plugin-clipboard-manager": "^2.3.2", "@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-fs": "^2.5.1", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 649aa7c..9ca9b5f 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2758,7 +2758,7 @@ dependencies = [ "jni-sys 0.4.1", "log", "simd_cesu8", - "thiserror 2.0.18", + "thiserror 2.0.19", "walkdir", "windows-link 0.2.1", ] diff --git a/src-tauri/src/commands/voice.rs b/src-tauri/src/commands/voice.rs index d981bf0..7b6c285 100644 --- a/src-tauri/src/commands/voice.rs +++ b/src-tauri/src/commands/voice.rs @@ -451,7 +451,7 @@ pub fn voice_is_recording() -> Result { } /// Stop the active recording, write the temporary WAV file, transcribe it -/// with whisper, and return the recognised text. +/// with whisper, and return the recognized text. #[tauri::command] pub fn voice_stop_recording() -> Result { let active = RECORDING diff --git a/src-tauri/src/git/mod.rs b/src-tauri/src/git/mod.rs index afed2ec..77fa13f 100644 --- a/src-tauri/src/git/mod.rs +++ b/src-tauri/src/git/mod.rs @@ -86,7 +86,7 @@ impl GitEngine { ))?; for entry in statuses.iter() { - if let Some(path) = entry.path() { + if let Ok(path) = entry.path() { let _ = index.add_path(Path::new(path)); } } @@ -198,7 +198,7 @@ impl GitEngine { let mut unstaged = Vec::new(); for entry in statuses.iter() { - if let Some(path) = entry.path() { + if let Ok(path) = entry.path() { unstaged.push(path.to_string()); } } @@ -207,15 +207,18 @@ impl GitEngine { let behind = 0; if let Ok(head) = repo.head() { - let head_oid = head.peel_to_commit().map(|c| c.id()).unwrap_or(Oid::zero()); - if let Some(branch_name) = head.shorthand() { + let head_oid = head + .peel_to_commit() + .map(|c| c.id()) + .unwrap_or(Oid::ZERO_SHA1); + if let Ok(branch_name) = head.shorthand() { if let Ok(branch) = repo.find_branch(branch_name, git2::BranchType::Local) { if let Ok(upstream) = branch.upstream() { let upstream_oid = upstream .get() .peel_to_commit() .map(|c| c.id()) - .unwrap_or(Oid::zero()); + .unwrap_or(Oid::ZERO_SHA1); let mut revwalk = repo.revwalk()?; revwalk.push(upstream_oid)?; diff --git a/src-tauri/src/mcp/mod.rs b/src-tauri/src/mcp/mod.rs index 6f9d94b..f13fa74 100644 --- a/src-tauri/src/mcp/mod.rs +++ b/src-tauri/src/mcp/mod.rs @@ -52,7 +52,7 @@ impl MCPClient { .collect() }) .unwrap_or_default(); - Box::new(transport::StdioTransport::new(&command, args)) + Box::new(transport::StdioTransport::new(&command, args)?) } TransportType::Http => { let url = config["url"] diff --git a/src-tauri/src/mcp/transport.rs b/src-tauri/src/mcp/transport.rs index debf6b9..4fdc490 100644 --- a/src-tauri/src/mcp/transport.rs +++ b/src-tauri/src/mcp/transport.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use async_trait::async_trait; use serde_json::Value; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; @@ -31,7 +31,7 @@ pub struct StdioTransport { } impl StdioTransport { - pub fn new(command: &str, args: Vec) -> Self { + pub fn new(command: &str, args: Vec) -> Result { let mut child = Command::new(command) .args(&args) .stdin(std::process::Stdio::piped()) @@ -39,22 +39,18 @@ impl StdioTransport { .stderr(std::process::Stdio::inherit()) .kill_on_drop(true) .spawn() - .unwrap_or_else(|e| { - // Log the error rather than panicking — callers handle connection failure - tracing::error!("Failed to spawn MCP subprocess '{}': {}", command, e); - panic!("MCP subprocess spawn failed: {}", e) - }); - - let stdin = child.stdin.take().expect("Failed to capture stdin"); - let stdout = child.stdout.take().expect("Failed to capture stdout"); + .with_context(|| format!("Failed to spawn MCP subprocess '{}'", command))?; + + let stdin = child.stdin.take().context("Failed to capture stdin")?; + let stdout = child.stdout.take().context("Failed to capture stdout")?; let reader = BufReader::new(stdout).lines(); - Self { + Ok(Self { process: Mutex::new(child), stdin: Mutex::new(stdin), stdout: Mutex::new(reader), request_id: Mutex::new(0), - } + }) } async fn next_id(&self) -> u64 { @@ -265,3 +261,23 @@ impl McpTransport for HttpTransport { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::StdioTransport; + + #[test] + fn stdio_transport_spawn_failure_returns_error() { + let command = format!( + "zarishnote-definitely-missing-mcp-server-{}", + std::process::id() + ); + + let result = StdioTransport::new(&command, Vec::new()); + + assert!( + result.is_err(), + "invalid MCP command should return an error" + ); + } +} diff --git a/src-tauri/tests/README.md b/src-tauri/tests/README.md index d6580cc..63de453 100644 --- a/src-tauri/tests/README.md +++ b/src-tauri/tests/README.md @@ -46,7 +46,7 @@ RUST_LOG=debug cargo test --test integration | `test_git_engine` | Commit‑message generation (`generate_commit_message`), change‑type detection | | `test_mcp_protocol` | JSON‑RPC parsing (request, response, error, notification), helper builders, `McpToolRouter` routing and confirmation logic | | `test_sandbox_executor` | `CapabilityChecker` (permissions, network, path), `glob_match`, `SandboxNetworkProxy` allow/block logic | -| `test_vector_store` | `VectorStore` index/query/delete/rebuild, chunking, deduplication, empty‑store behaviour | +| `test_vector_store` | `VectorStore` index/query/delete/rebuild, chunking, deduplication, empty‑store behavior | ## Design Principles @@ -55,6 +55,6 @@ RUST_LOG=debug cargo test --test integration 2. **No hard‑coded paths** — all filesystem operations use `tempfile::TempDir`. 3. **Public API only** — integration tests exercise the public interface of `zs_note_lib`; private implementation details are left to unit tests. -4. **Focused** — each test covers exactly one behaviour. +4. **Focused** — each test covers exactly one behavior. 5. **Deterministic** — tests are self‑contained and do not rely on global state or environment variables. diff --git a/src-tauri/tests/integration/test_config.rs b/src-tauri/tests/integration/test_config.rs index 099f2d4..defe450 100644 --- a/src-tauri/tests/integration/test_config.rs +++ b/src-tauri/tests/integration/test_config.rs @@ -1,6 +1,6 @@ //! Integration tests for configuration management. //! -//! Covers default construction, YAML round‑trip serialisation and +//! Covers default construction, YAML round‑trip serialization and //! the validation logic on [`Config`](zs_note_lib::config::Config). use std::path::Path; diff --git a/src-tauri/tests/integration/test_vector_store.rs b/src-tauri/tests/integration/test_vector_store.rs index 6c77a05..752f458 100644 --- a/src-tauri/tests/integration/test_vector_store.rs +++ b/src-tauri/tests/integration/test_vector_store.rs @@ -2,7 +2,7 @@ //! //! Tests the public API of [`zs_note_lib::vector`]: //! indexing, querying, deletion, rebuild, deduplication and -//! empty‑store behaviour. +//! empty‑store behavior. use std::fs; use std::path::{Path, PathBuf}; @@ -61,17 +61,23 @@ fn test_vector_store_index_and_query() { fn test_vector_store_query_multiple_docs() { let (_dir, store) = setup_temp_store(); + let alpha = PathBuf::from("alpha.md"); + let beta = PathBuf::from("beta.md"); + + store.index_document(&alpha, "The quick brown fox").unwrap(); store - .index_document(&PathBuf::from("alpha.md"), "The quick brown fox") - .unwrap(); - store - .index_document(&PathBuf::from("beta.md"), "jumps over the lazy dog") + .index_document(&beta, "jumps over the lazy dog") .unwrap(); - // Both documents share no common words; "fox" only appears in alpha + // "fox" only appears in alpha, so the query should return alpha exactly. let results = store.query("fox", "default", 10); assert_eq!(results.len(), 1, "only alpha should match 'fox'"); - assert!(results[0].path.to_string_lossy().contains("alpha")); + assert_eq!(results[0].path, alpha); + + // "dog" only appears in beta, so the query should return beta exactly. + let results = store.query("dog", "default", 10); + assert_eq!(results.len(), 1, "only beta should match 'dog'"); + assert_eq!(results[0].path, beta); } // ---------------------------------------------------------------------------