feat: consolidate local-first mnote web runtime
This commit is contained in:
@@ -2,11 +2,11 @@ use super::hermes_client;
|
||||
use crate::app::AppState;
|
||||
use crate::context::RequestContext;
|
||||
use crate::error::WebError;
|
||||
use crate::hermes_tools::{artifact, block, doc, manifest, page, resource, ToolCallInput};
|
||||
use crate::hermes_tools::{ToolCallInput, artifact, block, doc, manifest, page, resource};
|
||||
use axum::Json;
|
||||
use axum::extract::{Extension, Query, State};
|
||||
use axum::http::{HeaderMap, HeaderName, HeaderValue, StatusCode};
|
||||
use axum::Json;
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
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::{build_app, AppConfig, AppState};
|
||||
use axum::body::{to_bytes, Body};
|
||||
use crate::app::{AppConfig, AppState, build_app};
|
||||
use axum::body::{Body, to_bytes};
|
||||
use axum::http::{Request, StatusCode};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
use std::sync::Mutex;
|
||||
@@ -1112,36 +1112,56 @@ 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")
|
||||
@@ -1189,16 +1209,20 @@ 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]
|
||||
@@ -1228,14 +1252,18 @@ 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)
|
||||
@@ -1426,10 +1454,12 @@ 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);
|
||||
}
|
||||
@@ -1765,10 +1795,12 @@ 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");
|
||||
}
|
||||
|
||||
@@ -1814,10 +1846,12 @@ 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]
|
||||
@@ -1944,10 +1978,12 @@ 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");
|
||||
}
|
||||
|
||||
@@ -2210,10 +2246,12 @@ 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(
|
||||
@@ -2565,9 +2603,11 @@ 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()
|
||||
|
||||
Reference in New Issue
Block a user