using System.Text.Json; using YZWater.Core.Models; using YZWater.Core.Services; namespace YZWater.Core.Tests.Services; /// /// 配置文件路径锚定 + 敏感项落盘加密的回归测试 /// public class ConfigProtectionTests { private const string Token = "Hub-Token-S3cr3t"; [Fact] public void HubToken_SerializedAsCiphertext() { if (!OperatingSystem.IsWindows()) return; // DPAPI 不可用时按明文降级 var config = new SystemConfig { HubToken = Token }; var json = JsonSerializer.Serialize(config); Assert.DoesNotContain(Token, json); Assert.Contains("enc:", json); } [Fact] public void HubToken_SurvivesJsonRoundTrip() { if (!OperatingSystem.IsWindows()) return; var json = JsonSerializer.Serialize(new SystemConfig { HubToken = Token }); ProtectedStringJsonConverter.BeginRead(); var restored = JsonSerializer.Deserialize(json); Assert.Equal(Token, restored?.HubToken); Assert.False(ProtectedStringJsonConverter.NeedsRewrite); } [Fact] public void LegacyPlaintextHubToken_StillReadableAndMarkedForRewrite() { ProtectedStringJsonConverter.BeginRead(); var restored = JsonSerializer.Deserialize("{\"HubToken\":\"" + Token + "\"}"); Assert.Equal(Token, restored?.HubToken); Assert.True(ProtectedStringJsonConverter.NeedsRewrite); // ConfigService 据此回写一次,第二次加载就不该再要求改写 var rewritten = JsonSerializer.Serialize(restored); ProtectedStringJsonConverter.BeginRead(); var reloaded = JsonSerializer.Deserialize(rewritten); Assert.Equal(Token, reloaded?.HubToken); Assert.False(ProtectedStringJsonConverter.NeedsRewrite); } [Fact] public void UndecryptableCiphertext_FallsBackToEmptyInsteadOfThrowing() { // 配置文件从别的机器/别的 Windows 用户拷来时会走到这里 ProtectedStringJsonConverter.BeginRead(); var restored = JsonSerializer.Deserialize("{\"HubToken\":\"enc:not-valid-cipher-text\"}"); Assert.True(string.IsNullOrEmpty(restored?.HubToken)); Assert.True(ProtectedStringJsonConverter.DecryptFailed); // 关键:解不开不等于"等待迁移"。若此处要求回写,ConfigService 会把这段密文 // 覆盖成空值(还会顺手污染 .lastgood),操作员原机上配的令牌就彻底没了。 Assert.False(ProtectedStringJsonConverter.NeedsRewrite); } [Fact] public void LegacyPlaintext_MarksRewriteButNotDecryptFailure() { ProtectedStringJsonConverter.BeginRead(); JsonSerializer.Deserialize("{\"HubToken\":\"" + Token + "\"}"); Assert.True(ProtectedStringJsonConverter.NeedsRewrite); Assert.False(ProtectedStringJsonConverter.DecryptFailed); } [Fact] public void EmptyHubToken_StaysEmptyAndNeedsNoRewrite() { ProtectedStringJsonConverter.BeginRead(); var restored = JsonSerializer.Deserialize( JsonSerializer.Serialize(new SystemConfig { HubToken = string.Empty })); Assert.Equal(string.Empty, restored?.HubToken); Assert.False(ProtectedStringJsonConverter.NeedsRewrite); } }