using Apq.Cfg; namespace Apq.Cfg.Tests; /// /// Source Generator 生成的配置类测试 /// public class SourceGeneratorTests : IDisposable { private readonly string _testDir; public SourceGeneratorTests() { _testDir = Path.Combine(Path.GetTempPath(), $"ApqCfgSgTests_{Guid.NewGuid():N}"); Directory.CreateDirectory(_testDir); } [Fact] public void BindFrom_SimpleProperties_ShouldBindCorrectly() { // Arrange var jsonPath = Path.Combine(_testDir, "simple.json"); File.WriteAllText(jsonPath, @"{ ""Database"": { ""ConnectionString"": ""Server=localhost;Database=test"", ""Timeout"": 30, ""MaxRetries"": 3, ""EnableLogging"": true } }"); var cfg = new CfgBuilder() .AddJson(jsonPath, level: 0, writeable: false) .Build(); // Act var section = cfg.GetSection("Database"); var config = TestDatabaseConfig.BindFrom(section); // Assert Assert.Equal("Server=localhost;Database=test", config.ConnectionString); Assert.Equal(30, config.Timeout); Assert.Equal(3, config.MaxRetries); Assert.True(config.EnableLogging); } [Fact] public void BindFrom_NestedObject_ShouldBindCorrectly() { // Arrange var jsonPath = Path.Combine(_testDir, "nested.json"); File.WriteAllText(jsonPath, @"{ ""App"": { ""Name"": ""TestApp"", ""Version"": ""1.0.0"", ""Database"": { ""ConnectionString"": ""Server=db"", ""Timeout"": 60, ""MaxRetries"": 5, ""EnableLogging"": false } } }"); var cfg = new CfgBuilder() .AddJson(jsonPath, level: 0, writeable: false) .Build(); // Act var section = cfg.GetSection("App"); var config = TestAppConfig.BindFrom(section); // Assert Assert.Equal("TestApp", config.Name); Assert.Equal("1.0.0", config.Version); Assert.NotNull(config.Database); Assert.Equal("Server=db", config.Database.ConnectionString); Assert.Equal(60, config.Database.Timeout); } [Fact] public void BindFrom_ArrayProperty_ShouldBindCorrectly() { // Arrange var jsonPath = Path.Combine(_testDir, "array.json"); File.WriteAllText(jsonPath, @"{ ""Server"": { ""Name"": ""WebServer"", ""Ports"": [80, 443, 8080], ""AllowedHosts"": [""localhost"", ""127.0.0.1"", ""example.com""] } }"); var cfg = new CfgBuilder() .AddJson(jsonPath, level: 0, writeable: false) .Build(); // Act var section = cfg.GetSection("Server"); var config = TestServerConfig.BindFrom(section); // Assert Assert.Equal("WebServer", config.Name); Assert.NotNull(config.Ports); Assert.Equal(3, config.Ports.Length); Assert.Equal(80, config.Ports[0]); Assert.Equal(443, config.Ports[1]); Assert.Equal(8080, config.Ports[2]); Assert.NotNull(config.AllowedHosts); Assert.Equal(3, config.AllowedHosts.Count); Assert.Contains("localhost", config.AllowedHosts); } [Fact] public void BindFrom_DictionaryProperty_ShouldBindCorrectly() { // Arrange var jsonPath = Path.Combine(_testDir, "dict.json"); File.WriteAllText(jsonPath, @"{ ""Features"": { ""Name"": ""FeatureFlags"", ""Flags"": { ""DarkMode"": true, ""BetaFeatures"": false, ""Analytics"": true } } }"); var cfg = new CfgBuilder() .AddJson(jsonPath, level: 0, writeable: false) .Build(); // Act var section = cfg.GetSection("Features"); var config = TestFeaturesConfig.BindFrom(section); // Assert Assert.Equal("FeatureFlags", config.Name); Assert.NotNull(config.Flags); Assert.Equal(3, config.Flags.Count); Assert.True(config.Flags["DarkMode"]); Assert.False(config.Flags["BetaFeatures"]); Assert.True(config.Flags["Analytics"]); } [Fact] public void BindFrom_NullableProperties_ShouldHandleNulls() { // Arrange var jsonPath = Path.Combine(_testDir, "nullable.json"); File.WriteAllText(jsonPath, @"{ ""Optional"": { ""RequiredValue"": ""test"", ""OptionalInt"": 42 } }"); var cfg = new CfgBuilder() .AddJson(jsonPath, level: 0, writeable: false) .Build(); // Act var section = cfg.GetSection("Optional"); var config = TestOptionalConfig.BindFrom(section); // Assert Assert.Equal("test", config.RequiredValue); Assert.Equal(42, config.OptionalInt); Assert.Null(config.OptionalString); } [Fact] public void BindTo_ExistingObject_ShouldUpdateProperties() { // Arrange var jsonPath = Path.Combine(_testDir, "update.json"); File.WriteAllText(jsonPath, @"{ ""Database"": { ""ConnectionString"": ""NewConnection"", ""Timeout"": 120 } }"); var cfg = new CfgBuilder() .AddJson(jsonPath, level: 0, writeable: false) .Build(); var existingConfig = new TestDatabaseConfig { ConnectionString = "OldConnection", Timeout = 30, MaxRetries = 5, EnableLogging = true }; // Act var section = cfg.GetSection("Database"); TestDatabaseConfig.BindTo(section, existingConfig); // Assert Assert.Equal("NewConnection", existingConfig.ConnectionString); Assert.Equal(120, existingConfig.Timeout); // 未在配置中指定的属性保持原值 Assert.Equal(5, existingConfig.MaxRetries); Assert.True(existingConfig.EnableLogging); } [Fact] public void BindFrom_EmptySection_ShouldReturnDefaultValues() { // Arrange var jsonPath = Path.Combine(_testDir, "empty.json"); File.WriteAllText(jsonPath, @"{}"); var cfg = new CfgBuilder() .AddJson(jsonPath, level: 0, writeable: false) .Build(); // Act var section = cfg.GetSection("NonExistent"); var config = TestDatabaseConfig.BindFrom(section); // Assert Assert.Null(config.ConnectionString); Assert.Equal(0, config.Timeout); Assert.Equal(0, config.MaxRetries); Assert.False(config.EnableLogging); } [Fact] public void GeneratedExtension_ShouldWork() { // Arrange var jsonPath = Path.Combine(_testDir, "ext.json"); File.WriteAllText(jsonPath, @"{ ""TestDatabase"": { ""ConnectionString"": ""ExtTest"", ""Timeout"": 99 } }"); var cfg = new CfgBuilder() .AddJson(jsonPath, level: 0, writeable: false) .Build(); // Act - 使用生成的扩展方法 var config = cfg.GetTestDatabaseConfig(); // Assert Assert.Equal("ExtTest", config.ConnectionString); Assert.Equal(99, config.Timeout); } public void Dispose() { try { if (Directory.Exists(_testDir)) { Directory.Delete(_testDir, true); } } catch { } } } // 测试用配置类 - 使用 [CfgSection] 特性标记 [CfgSection("TestDatabase")] public partial class TestDatabaseConfig { public string? ConnectionString { get; set; } public int Timeout { get; set; } public int MaxRetries { get; set; } public bool EnableLogging { get; set; } } [CfgSection("App")] public partial class TestAppConfig { public string? Name { get; set; } public string? Version { get; set; } public TestDatabaseConfig? Database { get; set; } } [CfgSection("Server")] public partial class TestServerConfig { public string? Name { get; set; } public int[]? Ports { get; set; } public List? AllowedHosts { get; set; } } [CfgSection("Features")] public partial class TestFeaturesConfig { public string? Name { get; set; } public Dictionary? Flags { get; set; } } [CfgSection("Optional")] public partial class TestOptionalConfig { public string? RequiredValue { get; set; } public int? OptionalInt { get; set; } public string? OptionalString { get; set; } }