This commit is contained in:
wanghu
2026-09-02 09:38:45 +08:00
commit 8774b6cc44
128 changed files with 9382 additions and 0 deletions
@@ -0,0 +1,187 @@
using Microsoft.Extensions.Configuration;
namespace Yh.SchClass.OpenApi.Tests;
/// <summary>
/// 联调调试用的环境配置。地址来源优先级:
/// <para>
/// 1. 环境变量(<c>YH_SCHCLASS_DEBUG_HOST</c> / <c>YH_SCHCLASS_DEBUG_PATH</c>);
/// 2. <c>appsettings.json</c>:先独立节 <c>yh:schclass:open</c>,再共享节 <c>yh:open</c> 的
/// <c>BaseHost</c> + <c>SchClassPath</c>;
/// 3. 内置默认 <see cref="DefaultHost"/> + <see cref="DefaultPath"/>。
/// </para>
/// <para>
/// 关闭联调:置 <c>YH_SCHCLASS_DEBUG_OFF=1</c>,用例全部空跑通过(CI 用)。
/// </para>
/// </summary>
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";
/// <summary>配置与配置文件都未提供时的兜底联调主机。</summary>
public const string DefaultHost = "http://base.yunhaixingtong.com";
/// <summary>配置与配置文件都未提供时的兜底应用路径。</summary>
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();
}
}
/// <summary>是否开启联调(默认开启;置 <c>YH_SCHCLASS_DEBUG_OFF=1</c> 可关闭,CI 用它空跑)。</summary>
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();
}
/// <summary>联调主机:环境变量 → yh:schclass:open:BaseHost → yh:open:BaseHost → 内置默认。</summary>
public static string Host =>
Env(HostVar)
?? Configured("yh:schclass:open:BaseHost")
?? Configured("yh:open:BaseHost")
?? DefaultHost;
/// <summary>联调基础路径:环境变量 → yh:schclass:open:BasePath → yh:open:SchClassPath → 内置默认。</summary>
public static string? Path =>
Env(PathVar)
?? Configured("yh:open:SchClassPath")
?? DefaultPath;
/// <summary>按当前配置构建门面。</summary>
public static SchClassOpenApi Create()
{
var options = new SchClassOpenApiOptions { BaseHost = Host, BasePath = Path };
return SchClassOpenApiFactory.Create(options);
}
/// <summary>联调目标地址(用于日志输出)。</summary>
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;
/// <summary>DbClass 分组号,缺省 3(对联调环境真实有效的分组)。</summary>
public static int? Group => int.TryParse(Env(GroupVar), out var g) ? g : null;
/// <summary>
/// 一键跑通全部端点(测试用例与 <c>DebugMain.Main</c> 调试入口共用)。
/// 缺省 ID 一律从真实数据推导:用捏造的 ID 会让端点恒成立 0 条,把真实问题伪装成"接口空"。
/// 返回失败端点名列表;<paramref name="log"/> 输出进度。
/// </summary>
public static async Task<List<string>> RunAllAsync(Action<string> 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<Task<int>> 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<string> { "--only" };
}
}
var failures = new List<string>();
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;
}
}