using Xunit; namespace Admin.NET.Plugin.AiDOP.Tests.DataPlatform; /// /// 推送凭据与租户基线的源码守卫。测试项目不连库,行为判断见 /// ;这里钉住调用侧不会把两张表拆开、 /// 也不会在解析失败时静默跳过。 /// public class InboundGrantAndTenantBaselineGuardTests { private static string RepoFile(params string[] parts) { 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(new[] { dir!.FullName }.Concat(parts).ToArray())); } [Fact] public void CreateGrant_WritesIdentityAndGrantInOneTransaction_AndClearsCache() { var source = RepoFile("server", "Plugins", "Admin.NET.Plugin.AiDOP", "DataPlatform", "MdpSourceMappingService.cs"); var method = SliceMethod(source, "CreateGrant"); var begin = method.IndexOf("BeginTran()", StringComparison.Ordinal); var commit = method.IndexOf("CommitTran()", StringComparison.Ordinal); Assert.True(begin >= 0 && commit > begin, "新建身份与授权行必须在同一事务里"); var body = method[begin..commit]; Assert.Contains("new SysOpenAccess", body); Assert.Contains("BindTenantId = tenant", body); Assert.Contains("new MdpInboundGrant", body); Assert.Contains("CacheConst.KeyOpenAccess", method); Assert.Contains("RejectCrossTenant", method); } [Fact] public void TenantCreate_LogsEveryFailedBaselineResolution() { var source = RepoFile("server", "Admin.NET.Core", "Service", "Tenant", "SysTenantService.cs"); Assert.Contains("类型未解析", source); Assert.Contains("未注册到容器", source); Assert.Contains("方法未找到", source); } [Fact] public void ReinitEndpoint_RequiresSuperAdmin_AndExplicitTenant() { var source = RepoFile("server", "Plugins", "Admin.NET.Plugin.AiDOP", "Infrastructure", "AidopTenantBaselineController.cs"); var method = SliceMethod(source, "Reinit"); Assert.Contains("RequireSuperAdmin".Replace("RequireSuperAdmin", "SuperAdmin"), method); Assert.Contains("ErrorCodeEnum.SA001", method); Assert.Contains("input.TenantId", method); Assert.DoesNotContain("_user.TenantId", method); } [Fact] public void BaselineSeed_SkipsRowsThatAlreadyExist() { var source = RepoFile("server", "Plugins", "Admin.NET.Plugin.AiDOP", "Infrastructure", "AidopTenantInitService.cs"); var guards = source.Split("if (exists) continue").Length - 1; Assert.True(guards >= 3, $"存在性检查不足,重跑可能重复插入:{guards}"); } private static string SliceMethod(string source, string name) { var at = source.IndexOf(name + "(", StringComparison.Ordinal); Assert.True(at >= 0, $"找不到方法 {name}"); var open = source.IndexOf('{', at); var depth = 0; for (var end = open; end < source.Length; end++) { if (source[end] == '{') depth++; else if (source[end] == '}' && --depth == 0) return source[open..(end + 1)]; } throw new InvalidOperationException($"方法 {name} 没有闭合"); } }