using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace Yh.SchClass.OpenApi.Tests; /// /// 配置读取测试:对应 appsettings.json 的真实结构 —— /// /// "yh": { "open": { /// "BaseHost": "http://base.yunhaixingtong.com", /// "BasePath": "/userbase", // UserBase 用,教务不用它 /// "SchClassPath": "/bschclass" // 教务用它 /// }} /// /// public class SchClassOpenApiConfigTests { private static IConfiguration BuildConfig(Dictionary kv) => new ConfigurationBuilder().AddInMemoryCollection(kv).Build()!; [Fact] public void 共享节_SchClassPath映射为教务BasePath() { var config = BuildConfig(new Dictionary { ["yh:open:BaseHost"] = "http://base.yunhaixingtong.com", ["yh:open:BasePath"] = "/userbase", ["yh:open:SchClassPath"] = "/bschclass", }); var options = new SchClassOpenApiOptions(); var shared = config.GetSection(SchClassOpenApiOptions.SharedSectionName); options.FillFromSharedSection(key => shared[key]); Assert.Equal("http://base.yunhaixingtong.com", options.BaseHost); // 关键:教务用 SchClassPath,不是共享节里的 BasePath(/userbase) Assert.Equal("/bschclass", options.BasePath); Assert.Equal("http://base.yunhaixingtong.com/bschclass", options.ResolveBaseUrl()); } [Fact] public void 共享节_路径带空格与斜杠可归一化() { var config = BuildConfig(new Dictionary { ["yh:open:BaseHost"] = "http://base.yunhaixingtong.com", ["yh:open:SchClassPath"] = " /bschclass/", // 模拟配置里常见的多余空格/尾斜杠 }); var options = new SchClassOpenApiOptions(); var shared = config.GetSection(SchClassOpenApiOptions.SharedSectionName); options.FillFromSharedSection(key => shared[key]); Assert.Equal("/bschclass", options.BasePath); Assert.Equal("http://base.yunhaixingtong.com/bschclass", options.ResolveBaseUrl()); } [Fact] public void 共享节_未配SchClassPath时BasePath保持空() { var config = BuildConfig(new Dictionary { ["yh:open:BaseHost"] = "http://base.yunhaixingtong.com", ["yh:open:BasePath"] = "/userbase", // 不允许被教务误用 }); var options = new SchClassOpenApiOptions(); var shared = config.GetSection(SchClassOpenApiOptions.SharedSectionName); options.FillFromSharedSection(key => shared[key]); Assert.Equal("http://base.yunhaixingtong.com", options.BaseHost); Assert.Null(options.BasePath); Assert.Equal("http://base.yunhaixingtong.com", options.ResolveBaseUrl()); } [Fact] public void 独立节优先_共享节仅回填缺失项() { var config = BuildConfig(new Dictionary { ["yh:schclass:open:BasePath"] = "/sch-only", // 独立节只给了路径 ["yh:open:BaseHost"] = "http://base.yunhaixingtong.com", ["yh:open:SchClassPath"] = "/bschclass", ["yh:open:AppKey"] = "100002", ["yh:open:AppSecret"] = "secret", }); var options = new SchClassOpenApiOptions(); config.GetSection(SchClassOpenApiOptions.SectionName).Bind(options); var shared = config.GetSection(SchClassOpenApiOptions.SharedSectionName); options.FillFromSharedSection(key => shared[key]); Assert.Equal("http://base.yunhaixingtong.com", options.BaseHost); // 从共享节回填 Assert.Equal("/sch-only", options.BasePath); // 独立节优先,不被覆盖 Assert.Equal("100002", options.AppKey); // AppKey 回填 Assert.Equal("secret", options.AppSecret); } [Fact] public void DI_注册后可解析门面且地址来自配置() { var config = BuildConfig(new Dictionary { ["yh:open:BaseHost"] = "http://base.yunhaixingtong.com", ["yh:open:SchClassPath"] = "/bschclass", }); var services = new ServiceCollection(); services.AddSchClassOpenApi(config); using var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>().Value; Assert.Equal("http://base.yunhaixingtong.com/bschclass", options.ResolveBaseUrl()); var facade = provider.GetRequiredService(); Assert.NotNull(facade.SchInfo); Assert.NotNull(facade.StuInfo); Assert.Equal(new Uri("http://base.yunhaixingtong.com/bschclass"), facade.HttpBaseAddress); } [Fact] public void DI_独立节配置整节生效() { var config = BuildConfig(new Dictionary { ["yh:schclass:open:BaseHost"] = "http://sch.example.com", ["yh:schclass:open:BasePath"] = "/sch", ["yh:schclass:open:ReadTimeoutMs"] = "3000", ["yh:open:BaseHost"] = "http://base.yunhaixingtong.com", }); var services = new ServiceCollection(); services.AddSchClassOpenApi(config); using var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>().Value; Assert.Equal("http://sch.example.com", options.BaseHost); Assert.Equal("/sch", options.BasePath); Assert.Equal(3000, options.ReadTimeoutMs); } }