188 lines
8.3 KiB
C#
188 lines
8.3 KiB
C#
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;
|
||
}
|
||
}
|