fix local markdown attachment regressions

This commit is contained in:
lix-2026
2026-05-29 11:13:05 +08:00
parent 1109e3c0d8
commit cbe789e034
63 changed files with 3249 additions and 1502 deletions
+77 -117
View File
@@ -2,11 +2,11 @@ use super::hermes_client;
use crate::app::AppState;
use crate::context::RequestContext;
use crate::error::WebError;
use crate::hermes_tools::{ToolCallInput, artifact, block, doc, manifest, page, resource};
use axum::Json;
use crate::hermes_tools::{artifact, block, doc, manifest, page, resource, ToolCallInput};
use axum::extract::{Extension, Query, State};
use axum::http::{HeaderMap, HeaderName, HeaderValue, StatusCode};
use serde_json::{Value, json};
use axum::Json;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::env;
use std::fs::{self, File, OpenOptions};
@@ -827,10 +827,10 @@ fn stamp_tool_headers() -> HeaderMap {
#[cfg(test)]
mod tests {
use crate::app::{AppConfig, AppState, build_app};
use axum::body::{Body, to_bytes};
use crate::app::{build_app, AppConfig, AppState};
use axum::body::{to_bytes, Body};
use axum::http::{Request, StatusCode};
use serde_json::{Value, json};
use serde_json::{json, Value};
use std::collections::BTreeMap;
use std::fs;
use std::sync::Mutex;
@@ -1112,56 +1112,36 @@ mod tests {
assert!(tools.iter().any(|tool| tool["name"] == "mnote.doc.fetch"));
assert!(tools.iter().any(|tool| tool["name"] == "mnote.doc.find"));
assert!(tools.iter().any(|tool| tool["name"] == "mnote.block.fetch"));
assert!(
tools
.iter()
.any(|tool| tool["name"] == "mnote.doc.plan_update")
);
assert!(
tools
.iter()
.any(|tool| tool["name"] == "mnote.block.replace")
);
assert!(
tools
.iter()
.any(|tool| tool["name"] == "mnote.block.insert_after")
);
assert!(
tools
.iter()
.any(|tool| tool["name"] == "mnote.block.delete")
);
assert!(
tools
.iter()
.any(|tool| tool["name"] == "mnote.block.move_after")
);
assert!(
tools
.iter()
.any(|tool| tool["name"] == "mnote.doc.apply_block_ops")
);
assert!(
tools
.iter()
.any(|tool| tool["name"] == "mnote.mindmap.fetch")
);
assert!(
tools
.iter()
.any(|tool| tool["name"] == "mnote.mindmap.apply_ops")
);
assert!(
tools
.iter()
.any(|tool| tool["name"] == "mnote.office.fetch_summary")
);
assert!(
tools
.iter()
.any(|tool| tool["name"] == "mnote.office.propose_changes")
);
assert!(tools
.iter()
.any(|tool| tool["name"] == "mnote.doc.plan_update"));
assert!(tools
.iter()
.any(|tool| tool["name"] == "mnote.block.replace"));
assert!(tools
.iter()
.any(|tool| tool["name"] == "mnote.block.insert_after"));
assert!(tools
.iter()
.any(|tool| tool["name"] == "mnote.block.delete"));
assert!(tools
.iter()
.any(|tool| tool["name"] == "mnote.block.move_after"));
assert!(tools
.iter()
.any(|tool| tool["name"] == "mnote.doc.apply_block_ops"));
assert!(tools
.iter()
.any(|tool| tool["name"] == "mnote.mindmap.fetch"));
assert!(tools
.iter()
.any(|tool| tool["name"] == "mnote.mindmap.apply_ops"));
assert!(tools
.iter()
.any(|tool| tool["name"] == "mnote.office.fetch_summary"));
assert!(tools
.iter()
.any(|tool| tool["name"] == "mnote.office.propose_changes"));
let page_save = tools
.iter()
.find(|tool| tool["name"] == "mnote.page.save")
@@ -1209,20 +1189,16 @@ mod tests {
}
assert!(markdown_edit["inputSchema"]["properties"]["operations"].is_object());
assert!(markdown_edit["inputSchema"]["properties"]["full_content"].is_object());
assert!(
markdown_edit["inputSchema"]["anyOf"]
.as_array()
.expect("anyOf")
.iter()
.any(|rule| rule["required"] == json!(["operations"]))
);
assert!(
markdown_edit["inputSchema"]["anyOf"]
.as_array()
.expect("anyOf")
.iter()
.any(|rule| rule["required"] == json!(["full_content"]))
);
assert!(markdown_edit["inputSchema"]["anyOf"]
.as_array()
.expect("anyOf")
.iter()
.any(|rule| rule["required"] == json!(["operations"])));
assert!(markdown_edit["inputSchema"]["anyOf"]
.as_array()
.expect("anyOf")
.iter()
.any(|rule| rule["required"] == json!(["full_content"])));
}
#[tokio::test]
@@ -1252,18 +1228,14 @@ mod tests {
.find(|tool| tool["name"] == "mnote.page.save")
.expect("page save tool");
assert!(
markdown_edit["description"]
.as_str()
.expect("description")
.contains("兼容")
);
assert!(
markdown_edit["description"]
.as_str()
.expect("description")
.contains("agent 原生 patch/diff")
);
assert!(markdown_edit["description"]
.as_str()
.expect("description")
.contains("兼容"));
assert!(markdown_edit["description"]
.as_str()
.expect("description")
.contains("agent 原生 patch/diff"));
assert_eq!(
page_save["annotations"]["requiresWritePermission"],
Value::Bool(true)
@@ -1454,12 +1426,10 @@ mod tests {
payload["result"]["objectIdentity"],
"resource:mindmap:local-md:README.md:mind_allowed"
);
assert!(
payload["result"]["markdownSummary"]
.as_str()
.unwrap_or_default()
.contains("中心主题")
);
assert!(payload["result"]["markdownSummary"]
.as_str()
.unwrap_or_default()
.contains("中心主题"));
assert_eq!(payload["result"]["nodes"][1]["text"], "分支一");
let _ = fs::remove_dir_all(&root);
}
@@ -1795,12 +1765,10 @@ mod tests {
assert_eq!(payload["toolName"], "mnote.page.get");
assert_eq!(payload["toolCallId"], "call_1");
assert_eq!(payload["result"]["title"], "服务端页面");
assert!(
payload["result"]["bodySummary"]
.as_str()
.unwrap_or_default()
.contains("章节一")
);
assert!(payload["result"]["bodySummary"]
.as_str()
.unwrap_or_default()
.contains("章节一"));
assert_eq!(payload["audit"]["effect"], "read");
}
@@ -1846,12 +1814,10 @@ mod tests {
json!("heading_1")
);
assert_eq!(payload["result"]["blocks"][0]["text"], json!("章节一"));
assert!(
payload["result"]["blocks"][0]["revisionRef"]
.as_str()
.unwrap_or_default()
.starts_with("pageRev:7:block:heading_1:hash:")
);
assert!(payload["result"]["blocks"][0]["revisionRef"]
.as_str()
.unwrap_or_default()
.starts_with("pageRev:7:block:heading_1:hash:"));
}
#[tokio::test]
@@ -1978,12 +1944,10 @@ mod tests {
assert_eq!(payload["result"]["scope"], "selection");
assert_eq!(payload["result"]["format"], "page_xml");
assert_eq!(payload["result"]["blocks"].as_array().unwrap().len(), 1);
assert!(
payload["result"]["content"]
.as_str()
.unwrap_or_default()
.contains("<block id=\"heading_1\"")
);
assert!(payload["result"]["content"]
.as_str()
.unwrap_or_default()
.contains("<block id=\"heading_1\""));
assert_eq!(payload["result"]["allowedTargetBlockIds"][0], "heading_1");
}
@@ -2246,12 +2210,10 @@ mod tests {
insert_payload["result"]["changedBlocks"][0]["op"],
json!("insert_after")
);
assert!(
insert_payload["result"]["changedBlocks"][0]["blockId"]
.as_str()
.unwrap_or_default()
.starts_with("ai_block_")
);
assert!(insert_payload["result"]["changedBlocks"][0]["blockId"]
.as_str()
.unwrap_or_default()
.starts_with("ai_block_"));
let delete_response = app()
.oneshot(
@@ -2603,11 +2565,9 @@ mod tests {
.as_array()
.expect("insertedBlockIds");
assert_eq!(inserted_ids.len(), 2);
assert!(
inserted_ids
.iter()
.all(|id| { id.as_str().unwrap_or_default().starts_with("ai_block_") })
);
assert!(inserted_ids
.iter()
.all(|id| { id.as_str().unwrap_or_default().starts_with("ai_block_") }));
assert_eq!(
payload["result"]["changedBlocks"]
.as_array()