using Microsoft.Extensions.Configuration; namespace Yh.SchClass.OpenApi.Tests; /// /// 联调调试用的环境配置。地址来源优先级: /// /// 1. 环境变量(YH_SCHCLASS_DEBUG_HOST / YH_SCHCLASS_DEBUG_PATH); /// 2. appsettings.json:先独立节 yh:schclass:open,再共享节 yh:open 的 /// BaseHost + SchClassPath; /// 3. 内置默认 + 。 /// /// /// 关闭联调:置 YH_SCHCLASS_DEBUG_OFF=1,用例全部空跑通过(CI 用)。 /// /// internal static class SchClassLiveDebug { public const string HostVar = "YH_SCHCLASS_DEBUG_HOST"; public const string PathVar = "YH_SCHCLASS_DEBUG_PATH"; public const string SchVar = "YH_SCHCLASS_DEBUG_SCH"; public const string ClassVar = "YH_SCHCLASS_DEBUG_CLASS"; public const string TeaVar = "YH_SCHCLASS_DEBUG_TEA"; public const string GroupVar = "YH_SCHCLASS_DEBUG_GROUP"; public const string OffVar = "YH_SCHCLASS_DEBUG_OFF"; /// 配置与配置文件都未提供时的兜底联调主机。 public const string DefaultHost = "http://base.yunhaixingtong.com"; /// 配置与配置文件都未提供时的兜底应用路径。 public const string DefaultPath = "/bschclass"; private static readonly IConfiguration? Config; static SchClassLiveDebug() { var file = System.IO.Path.Combine(AppContext.BaseDirectory, "appsettings.json"); if (File.Exists(file)) { Config = new ConfigurationBuilder().AddJsonFile(file, optional: true).Build(); } } /// 是否开启联调(默认开启;置 YH_SCHCLASS_DEBUG_OFF=1 可关闭,CI 用它空跑)。 public static bool Enabled { get { var off = Environment.GetEnvironmentVariable(OffVar); return !(off == "1" || "true".Equals(off, StringComparison.OrdinalIgnoreCase)); } } private static string? Env(string name) { var value = Environment.GetEnvironmentVariable(name); return string.IsNullOrWhiteSpace(value) ? null : value.Trim(); } private static string? Configured(string path) { var value = Config?[path]; return string.IsNullOrWhiteSpace(value) ? null : value.Trim(); } /// 联调主机:环境变量 → yh:schclass:open:BaseHost → yh:open:BaseHost → 内置默认。 public static string Host => Env(HostVar) ?? Configured("yh:schclass:open:BaseHost") ?? Configured("yh:open:BaseHost") ?? DefaultHost; /// 联调基础路径:环境变量 → yh:schclass:open:BasePath → yh:open:SchClassPath → 内置默认。 public static string? Path => Env(PathVar) ?? Configured("yh:open:SchClassPath") ?? DefaultPath; /// 按当前配置构建门面。 public static SchClassOpenApi Create() { var options = new SchClassOpenApiOptions { BaseHost = Host, BasePath = Path }; return SchClassOpenApiFactory.Create(options); } /// 联调目标地址(用于日志输出)。 public static string Target() { var options = new SchClassOpenApiOptions { BaseHost = Host, BasePath = Path }; return options.ResolveBaseUrl(); } public static string? Sch => Env(SchVar); public static string? ClassId => Env(ClassVar); public static int? TeaSerId => int.TryParse(Env(TeaVar), out var id) ? id : null; /// DbClass 分组号,缺省 3(对联调环境真实有效的分组)。 public static int? Group => int.TryParse(Env(GroupVar), out var g) ? g : null; /// /// 一键跑通全部端点(测试用例与 DebugMain.Main 调试入口共用)。 /// 缺省 ID 一律从真实数据推导:用捏造的 ID 会让端点恒成立 0 条,把真实问题伪装成"接口空"。 /// 返回失败端点名列表; 输出进度。 /// public static async Task> RunAllAsync(Action log, string? only = null) { var api = Create(); var sch = Sch ?? (await api.SchInfo.AllListAsync()).FirstOrDefault()?.SchSerId ?? "S001"; var group = Group ?? 3; var classId = ClassId; if (classId == null) { var cls = (await api.VClass.GetVClassListAsync(sch)).FirstOrDefault(); classId = cls != null && cls.TryGetValue("ClassID", out var cid) ? cid.ToString() : "1001"; } var teaId = TeaSerId; if (teaId == null) { var tea = (await api.DbClass.GetSchteaListAsync(group)).FirstOrDefault(); teaId = tea != null && tea.TryGetValue("UserTeaSerID", out var tid) && int.TryParse(tid.ToString(), out var parsed) ? parsed : 77; } var cases = new List<(string Name, Func> Call)> { // Group=3 对应联调环境真实存在的分组;可用 YH_SCHCLASS_DEBUG_GROUP 覆盖 ("DbClass.GetSchteaList", async () => (await api.DbClass.GetSchteaListAsync(group)).Count), ("VSchClass.GetVClassList", async () => (await api.VClass.GetVClassListAsync(sch)).Count), ("VSchClass.GetSchArrVClassListAll", async () => (await api.VClass.GetSchArrVClassListAllAsync()).Count), ("VSchClass.GetSchArrVClassListPost", async () => (await api.VClass.GetSchArrVClassListPostAsync(sch)).Count), ("Schoolen.GetSchLenLevellist", async () => (await api.Schooling.GetSchLenLevelListAsync(sch)).Count), ("Schoolen.GetSchLenlist", async () => (await api.Schooling.GetSchLenListAsync(sch)).Count), ("UserInfo.GetUserList", async () => (await api.UserInfo.GetUserListAsync(sch)).Count), ("SchInfo.AllList", async () => (await api.SchInfo.AllListAsync()).Count), ("SchInfo.GetSchlist", async () => (await api.SchInfo.GetSchListAsync(null)).Count), ("OATea.GetSchTeainfo", async () => (await api.TeaInfo.GetSchTeaInfoAsync(sch, null)).Count), ("OATea.GetArrTeainfo", async () => (await api.TeaInfo.GetArrTeaInfoAsync(teaId.Value.ToString())).Count), ("OAStu.GetAllStulist", async () => (await api.StuInfo.GetAllStuListAsync()).Count), ("OAStu.GetSchStulist", async () => (await api.StuInfo.GetSchStuListAsync(sch, null)).Count), ("OAStu.GetClaStulist", async () => (await api.StuInfo.GetClaStuListAsync(classId, null)).Count), ("TeachInfo.GetSchTeachlist", async () => (await api.TeachInfo.GetSchTeachListAsync(sch, teaId)).Count), ("TeachInfo.GetSchClaTeachlist", async () => (await api.TeachInfo.GetSchClaTeachListAsync(classId)).Count), ("SubInfo.GetSchTeachSublist", async () => (await api.SubInfo.GetSchTeachSubListAsync(sch)).Count), ("SubInfo.GetSchSublist", async () => (await api.SubInfo.GetSchSubListAsync(sch, null, null)).Count), ("FamCom.GetStuUserFamIDList", async () => (await api.FamilyCompat.GetStuUserFamIdListAsync("1,2")).Count), ("FamCom.GetAllStuFamMem", async () => (await api.FamilyCompat.GetAllStuFamMemAsync("1,2")).Count), }; if (only != null) { cases = cases.Where(c => c.Name.Contains(only, StringComparison.OrdinalIgnoreCase)).ToList(); if (cases.Count == 0) { log($"--only={only} 未匹配任何端点"); return new List { "--only" }; } } var failures = new List(); log($"联调目标:{Target()} sch={sch} classId={classId} teaId={teaId} group={group}"); foreach (var (name, call) in cases) { try { var count = await call(); log($" OK {name} -> {count} 条"); } catch (Exception e) { failures.Add(name); log($" FAIL {name} -> {e.GetType().Name}: {e.Message}"); } } log($"汇总:{cases.Count - failures.Count}/{cases.Count} 通过"); return failures; } }