chore: apply cargo fmt across workspace and update docs

- Run cargo fmt on all Rust crates for consistent formatting
- Update CLAUDE.md with WASM plugin commands and dev.ps1 instructions
- Update wiki: add WASM plugin architecture, rewrite dev environment docs
- Minor frontend cleanup (unused imports)
This commit is contained in:
iven
2026-04-15 00:49:20 +08:00
parent e16c1a85d7
commit 9568dd7875
113 changed files with 4355 additions and 937 deletions

View File

@@ -0,0 +1,220 @@
//! WASM 插件集成测试
//!
//! 验证目标:
//! V1 — WIT 接口 + bindgen! 编译通过
//! V2 — Host 调用插件 init() / handle_event()
//! V3 — 插件回调 Host db_insert / log_write
//! V4 — async 实例化桥接
//! V5 — Fuel 资源限制
//! V6 — 从二进制动态加载
use anyhow::Result;
use erp_plugin_prototype::{create_engine, load_plugin};
/// 获取测试插件 WASM Component 文件路径
fn wasm_path() -> String {
let candidates = [
// 预构建的 WASM Component通过 wasm-tools component new 生成)
"../../target/erp_plugin_test_sample.component.wasm".into(),
// 备选:绝对路径
format!(
"{}/../../target/erp_plugin_test_sample.component.wasm",
std::env::current_dir().unwrap().display()
),
];
for path in &candidates {
if std::path::Path::new(path).exists() {
return path.clone();
}
}
candidates[0].clone()
}
#[tokio::test]
async fn test_v6_load_plugin_from_binary() -> Result<()> {
let wasm_path = wasm_path();
let wasm_bytes = std::fs::read(&wasm_path).map_err(|e| {
anyhow::anyhow!(
"读取 WASM 失败: {}。请先编译: cargo build -p erp-plugin-test-sample --target wasm32-unknown-unknown --release\n路径: {}",
e, wasm_path
)
})?;
assert!(!wasm_bytes.is_empty(), "WASM 文件不应为空");
println!("WASM 文件大小: {} bytes", wasm_bytes.len());
let engine = create_engine()?;
let (_store, _instance) = load_plugin(&engine, &wasm_bytes, 1_000_000).await?;
Ok(())
}
#[tokio::test]
async fn test_v2_host_calls_plugin_init() -> Result<()> {
let wasm_bytes = std::fs::read(wasm_path())?;
let engine = create_engine()?;
let (mut store, instance) = load_plugin(&engine, &wasm_bytes, 1_000_000).await?;
// V2: 调用插件 init()
instance
.erp_plugin_plugin_api()
.call_init(&mut store)?
.map_err(|e| anyhow::anyhow!(e))?;
// 验证 Host 端收到了日志
let state = store.data();
assert!(
state
.logs
.iter()
.any(|(_, m)| m.contains("测试插件初始化成功")),
"应收到初始化日志"
);
Ok(())
}
#[tokio::test]
async fn test_v3_plugin_calls_host_api() -> Result<()> {
let wasm_bytes = std::fs::read(wasm_path())?;
let engine = create_engine()?;
let (mut store, instance) = load_plugin(&engine, &wasm_bytes, 1_000_000).await?;
// 先 init插件会调用 db_insert 和 log_write
instance
.erp_plugin_plugin_api()
.call_init(&mut store)?
.map_err(|e| anyhow::anyhow!(e))?;
let state = store.data();
// 验证 db_insert 被调用
assert!(
state
.db_ops
.iter()
.any(|op| op.op_type == "insert" && op.entity == "inventory_item"),
"应有 inventory_item 的 insert 操作"
);
// 验证 log_write 被调用
assert!(
state.logs.iter().any(|(_, m)| m.contains("插入成功")),
"应有插入成功的日志"
);
Ok(())
}
#[tokio::test]
async fn test_v3_plugin_handle_event_with_db_callback() -> Result<()> {
let wasm_bytes = std::fs::read(wasm_path())?;
let engine = create_engine()?;
let (mut store, instance) = load_plugin(&engine, &wasm_bytes, 1_000_000).await?;
// 先 init
instance
.erp_plugin_plugin_api()
.call_init(&mut store)?
.map_err(|e| anyhow::anyhow!(e))?;
// 发送事件
let payload = serde_json::json!({"order_id": "PO-001", "action": "approve"}).to_string();
instance
.erp_plugin_plugin_api()
.call_handle_event(&mut store, "workflow.task.completed", payload.as_bytes())?
.map_err(|e| anyhow::anyhow!(e))?;
let state = store.data();
// 验证 db_update 被调用
assert!(
state
.db_ops
.iter()
.any(|op| op.op_type == "update" && op.entity == "purchase_order"),
"应有 purchase_order 的 update 操作"
);
// 验证事件被发布
assert!(
state
.events
.iter()
.any(|(t, _)| t == "purchase_order.approved"),
"应发布 purchase_order.approved 事件"
);
Ok(())
}
#[tokio::test]
async fn test_v5_fuel_limit_traps() -> Result<()> {
let wasm_bytes = std::fs::read(wasm_path())?;
let engine = create_engine()?;
// 给极少量的 fuel插件 init() 应该无法完成
let result = load_plugin(&engine, &wasm_bytes, 10).await;
match result {
Ok((mut store, instance)) => {
let init_result = instance.erp_plugin_plugin_api().call_init(&mut store);
assert!(
init_result.is_err() || init_result.unwrap().is_err(),
"低 fuel 应导致失败"
);
}
Err(_) => {
// 实例化就失败了,也是预期行为
}
}
Ok(())
}
#[tokio::test]
async fn test_full_lifecycle() -> Result<()> {
let wasm_bytes = std::fs::read(wasm_path())?;
let engine = create_engine()?;
let (mut store, instance) = load_plugin(&engine, &wasm_bytes, 1_000_000).await?;
// 1. init
instance
.erp_plugin_plugin_api()
.call_init(&mut store)?
.map_err(|e| anyhow::anyhow!(e))?;
// 2. on_tenant_created
instance
.erp_plugin_plugin_api()
.call_on_tenant_created(&mut store, "tenant-new-001")?
.map_err(|e| anyhow::anyhow!(e))?;
// 3. handle_event
let payload = serde_json::json!({"order_id": "PO-002"}).to_string();
instance
.erp_plugin_plugin_api()
.call_handle_event(&mut store, "workflow.task.completed", payload.as_bytes())?
.map_err(|e| anyhow::anyhow!(e))?;
let state = store.data();
// 完整生命周期验证
assert!(
state.logs.len() >= 3,
"应有多条日志记录,实际: {}",
state.logs.len()
);
assert!(
state.db_ops.len() >= 3,
"应有多次数据库操作,实际: {}",
state.db_ops.len()
);
assert!(state.events.len() >= 1, "应有事件发布");
println!("\n=== 完整生命周期验证 ===");
println!("日志: {}", state.logs.len());
println!("DB 操作: {}", state.db_ops.len());
println!("事件: {}", state.events.len());
Ok(())
}