| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- using Xunit;
- namespace Admin.NET.Plugin.AiDOP.Tests.DataPlatform;
- /// <summary>
- /// 包 G 的越权守卫。测试项目不连库、没有认证夹具,所以用源文本扫描钉住三条不变式:
- /// 每个 admin 接口都拒绝非超管、都不走租户解析、都复用包 B 的指派写入侧。
- /// </summary>
- public class MdpMonitorAdminGuardTests
- {
- private static string MonitorSource()
- {
- var dir = new DirectoryInfo(AppContext.BaseDirectory);
- while (dir != null && !File.Exists(Path.Combine(dir.FullName, "AGENTS.md")))
- dir = dir.Parent;
- Assert.NotNull(dir);
- return File.ReadAllText(Path.Combine(
- dir!.FullName, "server", "Plugins", "Admin.NET.Plugin.AiDOP", "Order", "MdpMonitorService.cs"));
- }
- private static string RebuildSource()
- {
- var dir = new DirectoryInfo(AppContext.BaseDirectory);
- while (dir != null && !File.Exists(Path.Combine(dir.FullName, "AGENTS.md")))
- dir = dir.Parent;
- return File.ReadAllText(Path.Combine(
- dir!.FullName, "server", "Plugins", "Admin.NET.Plugin.AiDOP", "DataPlatform", "MdpRebuild", "ModuleRebuildService.cs"));
- }
- private static string WorkerSource()
- {
- var dir = new DirectoryInfo(AppContext.BaseDirectory);
- while (dir != null && !File.Exists(Path.Combine(dir.FullName, "AGENTS.md")))
- dir = dir.Parent;
- return File.ReadAllText(Path.Combine(
- dir!.FullName, "server", "Plugins", "Admin.NET.Plugin.AiDOP", "DataPlatform", "MdpRebuild", "ModuleRebuildWorker.cs"));
- }
- /// <summary>切出每个 admin 路由方法的方法体。</summary>
- private static IEnumerable<string> AdminMethodBodies(string source)
- {
- var marker = "mdp-monitor/admin/";
- var from = 0;
- while (true)
- {
- var at = source.IndexOf(marker, from, StringComparison.Ordinal);
- if (at < 0) yield break;
- var open = source.IndexOf('{', at);
- var depth = 0;
- var end = open;
- for (; end < source.Length; end++)
- {
- if (source[end] == '{') depth++;
- else if (source[end] == '}' && --depth == 0) break;
- }
- yield return source[open..(end + 1)];
- from = end + 1;
- }
- }
- [Fact]
- public void AdminEndpoints_AllCheckSuperAdmin()
- {
- var bodies = AdminMethodBodies(MonitorSource()).ToList();
- Assert.True(bodies.Count >= 6, $"admin 接口数量不符:{bodies.Count}");
- foreach (var body in bodies)
- Assert.Contains("RequireSuperAdmin()", body);
- }
- [Fact]
- public void AdminEndpoints_DoNotCallResolveOrThrow()
- {
- foreach (var body in AdminMethodBodies(MonitorSource()))
- Assert.DoesNotContain("ResolveOrThrow", body);
- }
- [Fact]
- public void LegacyEndpoints_StillCallResolveOrThrow()
- {
- var source = MonitorSource();
- foreach (var route in new[]
- {
- "mdp-monitor/outbound-gate", "mdp-monitor/ship-recon", "mdp-monitor/latest",
- "mdp-monitor/list", "mdp-monitor/detail", "mdp-monitor/lineage"
- })
- {
- var at = source.IndexOf($"\"{route}", StringComparison.Ordinal);
- Assert.True(at >= 0, $"原有路由丢失:{route}");
- var window = source[at..Math.Min(source.Length, at + 600)];
- Assert.Contains("ResolveOrThrow", window);
- }
- }
- [Fact]
- public void BuildMdpRunLogTenantWhere_Unchanged()
- {
- Assert.Equal("(tenant_id = @TenantId OR tenant_id = 0)",
- Admin.NET.Plugin.AiDOP.Order.MdpMonitorService.BuildMdpRunLogTenantWhere(9));
- Assert.Equal("tenant_id = 0",
- Admin.NET.Plugin.AiDOP.Order.MdpMonitorService.BuildMdpRunLogTenantWhere(0));
- }
- [Fact]
- public void CancelCheck_IsInProgressCallback_NotWorker()
- {
- Assert.Contains("IsCancelRequestedAsync", RebuildSource());
- Assert.DoesNotContain("IsCancelRequestedAsync", WorkerSource());
- Assert.DoesNotContain("cancel_requested", WorkerSource());
- }
- [Fact]
- public void CancelledCatch_PrecedesGenericCatch()
- {
- var source = RebuildSource();
- var specific = source.IndexOf("catch (ModuleRebuildCancelledException)", StringComparison.Ordinal);
- Assert.True(specific >= 0, "缺少取消异常的 catch");
- var generic = source.IndexOf("catch (Exception ex)", specific, StringComparison.Ordinal);
- Assert.True(generic > specific, "取消异常的 catch 必须早于通用 catch,否则会被吞成 FAILED");
- }
- [Fact]
- public void AssignRunner_ReusesPackageBStore()
- {
- var source = MonitorSource();
- Assert.Contains("_etlInstances.AssignRunnerAsync", source);
- Assert.DoesNotContain("SET is_runner", source);
- Assert.DoesNotContain("IsRunner = true", source);
- }
- [Fact]
- public void ModuleCatches_RethrowAdminCancelBeforeMarkingFailed()
- {
- var dir = new DirectoryInfo(AppContext.BaseDirectory);
- while (dir != null && !File.Exists(Path.Combine(dir.FullName, "AGENTS.md")))
- dir = dir.Parent;
- Assert.NotNull(dir);
- string[] files =
- [
- Path.Combine(dir!.FullName, "server", "Plugins", "Admin.NET.Plugin.AiDOP", "Order", "S1MdpSyncTransformService.cs"),
- Path.Combine(dir.FullName, "server", "Plugins", "Admin.NET.Plugin.AiDOP", "Production", "S2MdpSyncTransformService.cs"),
- Path.Combine(dir.FullName, "server", "Plugins", "Admin.NET.Plugin.AiDOP", "Supply", "S3MdpSyncTransformService.cs"),
- Path.Combine(dir.FullName, "server", "Plugins", "Admin.NET.Plugin.AiDOP", "ProcurementExecution", "S4MdpSyncTransformService.cs"),
- Path.Combine(dir.FullName, "server", "Plugins", "Admin.NET.Plugin.AiDOP", "MaterialWarehouse", "S5MdpSyncTransformService.cs"),
- Path.Combine(dir.FullName, "server", "Plugins", "Admin.NET.Plugin.AiDOP", "Manufacturing", "S6MdpSyncTransformService.cs"),
- Path.Combine(dir.FullName, "server", "Plugins", "Admin.NET.Plugin.AiDOP", "FinishedWarehouse", "S7MdpSyncTransformService.cs"),
- ];
- foreach (var file in files)
- {
- var source = File.ReadAllText(file);
- var failed = source.IndexOf("MarkTransformRunFailedAsync", StringComparison.Ordinal);
- var rethrow = source.IndexOf("ex is ModuleRebuildCancelledException", StringComparison.Ordinal);
- Assert.True(rethrow >= 0 && rethrow < failed, Path.GetFileName(file) + " 必须先把管理员取消抛回去,不能先写成 FAILED");
- }
- }
- }
|