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, } } /// 构建测试用 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(), 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 的数据"); }