Files
erp/crates/erp-server/tests/integration/plugin_tests.rs
iven 0ee9d22634
Some checks failed
CI / rust-check (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
CI / rust-test (push) Has been cancelled
fix(plugin): P1 跨插件引用修复 — DateTime generated column + resolve-labels UUID 类型 + EntitySelect manifest→UUID 映射
- manifest.rs: DateTime 类型 generated column 改为 TEXT 存储(PostgreSQL TIMESTAMPTZ cast 非 immutable)
- data_handler.rs: resolve-labels 查询参数从 String 改为 UUID 类型避免类型不匹配
- data_dto.rs: PublicEntityResp 新增 plugin_id 字段
- EntitySelect.tsx: 跨插件查询先通过 registry 解析 manifest_id→plugin UUID
- pluginData.ts: PublicEntity 接口增加 plugin_id
- plugin_tests.rs: 适配 PluginField/PluginEntity 新增字段
2026-04-19 08:44:45 +08:00

194 lines
6.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use erp_plugin::dynamic_table::DynamicTableManager;
use erp_plugin::manifest::{
PluginEntity, PluginField, PluginFieldType, PluginManifest, PluginMetadata, PluginSchema,
};
use sea_orm::{ConnectionTrait, FromQueryResult};
use super::test_db::TestDb;
/// 构造一个最小默认值的 PluginField外部 crate 无法使用 #[cfg(test)] 的 default_for_field
fn make_field(name: &str, field_type: PluginFieldType) -> PluginField {
PluginField {
name: name.to_string(),
field_type,
required: false,
unique: false,
default: None,
display_name: None,
ui_widget: None,
options: None,
searchable: None,
filterable: None,
sortable: None,
visible_when: None,
ref_entity: None,
ref_label_field: None,
ref_search_fields: None,
cascade_from: None,
cascade_filter: None,
validation: None,
no_cycle: None,
scope_role: None,
ref_plugin: None,
ref_fallback_label: None,
}
}
/// 构建测试用 manifest
fn make_test_manifest() -> PluginManifest {
PluginManifest {
metadata: PluginMetadata {
id: "erp-test".to_string(),
name: "测试插件".to_string(),
version: "0.1.0".to_string(),
description: "集成测试用".to_string(),
author: "test".to_string(),
min_platform_version: None,
dependencies: vec![],
},
schema: Some(PluginSchema {
entities: vec![PluginEntity {
name: "item".to_string(),
display_name: "测试项".to_string(),
is_public: None,
fields: vec![
PluginField {
name: "code".to_string(),
field_type: PluginFieldType::String,
required: true,
unique: true,
display_name: Some("编码".to_string()),
searchable: Some(true),
..make_field("code", PluginFieldType::String)
},
PluginField {
name: "name".to_string(),
field_type: PluginFieldType::String,
required: true,
display_name: Some("名称".to_string()),
searchable: Some(true),
..make_field("name", PluginFieldType::String)
},
PluginField {
name: "status".to_string(),
field_type: PluginFieldType::String,
filterable: Some(true),
display_name: Some("状态".to_string()),
..make_field("status", PluginFieldType::String)
},
PluginField {
name: "sort_order".to_string(),
field_type: PluginFieldType::Integer,
sortable: Some(true),
display_name: Some("排序".to_string()),
..make_field("sort_order", PluginFieldType::Integer)
},
],
indexes: vec![],
relations: vec![],
data_scope: None,
}],
}),
events: None,
ui: None,
permissions: None,
}
}
#[tokio::test]
async fn test_dynamic_table_create_and_query() {
let test_db = TestDb::new().await;
let db = test_db.db();
let manifest = make_test_manifest();
let entity = &manifest.schema.as_ref().unwrap().entities[0];
// 创建动态表
DynamicTableManager::create_table(db, "erp_test", entity)
.await
.expect("创建动态表失败");
let table_name = DynamicTableManager::table_name("erp_test", &entity.name);
// 验证表存在
let exists = DynamicTableManager::table_exists(db, &table_name)
.await
.expect("检查表存在失败");
assert!(exists, "动态表应存在");
// 插入数据
let tenant_id = uuid::Uuid::new_v4();
let user_id = uuid::Uuid::new_v4();
let data = serde_json::json!({
"code": "ITEM001",
"name": "测试项目",
"status": "active",
"sort_order": 1
});
let (sql, values) = DynamicTableManager::build_insert_sql(&table_name, tenant_id, user_id, &data);
db.execute(sea_orm::Statement::from_sql_and_values(
sea_orm::DatabaseBackend::Postgres,
sql,
values,
))
.await
.expect("插入数据失败");
// 查询数据
let (sql, values) = DynamicTableManager::build_query_sql(&table_name, tenant_id, 10, 0);
#[derive(FromQueryResult)]
struct Row {
id: uuid::Uuid,
data: serde_json::Value,
}
let rows = Row::find_by_statement(sea_orm::Statement::from_sql_and_values(
sea_orm::DatabaseBackend::Postgres,
sql,
values,
))
.all(db)
.await
.expect("查询数据失败");
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].data["code"], "ITEM001");
assert_eq!(rows[0].data["name"], "测试项目");
}
#[tokio::test]
async fn test_tenant_isolation_in_dynamic_table() {
let test_db = TestDb::new().await;
let db = test_db.db();
let manifest = make_test_manifest();
let entity = &manifest.schema.as_ref().unwrap().entities[0];
DynamicTableManager::create_table(db, "erp_test_iso", entity)
.await
.expect("创建动态表失败");
let table_name = DynamicTableManager::table_name("erp_test_iso", &entity.name);
let tenant_a = uuid::Uuid::new_v4();
let tenant_b = uuid::Uuid::new_v4();
let user_id = uuid::Uuid::new_v4();
// 租户 A 插入数据
let data_a = serde_json::json!({"code": "A001", "name": "租户A数据", "status": "active", "sort_order": 1});
let (sql, values) = DynamicTableManager::build_insert_sql(&table_name, tenant_a, user_id, &data_a);
db.execute(sea_orm::Statement::from_sql_and_values(
sea_orm::DatabaseBackend::Postgres, sql, values,
)).await.unwrap();
// 租户 B 查询不应看到租户 A 的数据
let (sql, values) = DynamicTableManager::build_query_sql(&table_name, tenant_b, 10, 0);
#[derive(FromQueryResult)]
struct Row { id: uuid::Uuid, data: serde_json::Value }
let rows = Row::find_by_statement(sea_orm::Statement::from_sql_and_values(
sea_orm::DatabaseBackend::Postgres, sql, values,
)).all(db).await.unwrap();
assert!(rows.is_empty(), "租户 B 不应看到租户 A 的数据");
}