XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEExtension.NetCore.AutoConfig

【DLL】自动实现配置文件的存储

公开
关注 0 Fork 0 Star 0
UTF-8
using Xunit;

namespace XFEExtension.NetCore.AutoConfig.Tests;

public sealed class AdvancedPersistenceTests
{
    [Fact]
    public async Task VersionMigrationRenamesFieldAndPersistsCurrentVersion()
    {
        var directory = CreateTestDirectory();
        try
        {
            MigratingJsonProfile.ProfilePath = Path.Combine(directory, "migration");
            var path = MigratingJsonProfile.ProfilePath + ".json";
            await File.WriteAllTextAsync(path, "{\"InstanceLegacyName\":\"migrated\"}");

            MigratingJsonProfile.LoadProfile();

            Assert.Equal("migrated", MigratingJsonProfile.DisplayName);
            Assert.Equal(7, MigratingJsonProfile.Revision);
            Assert.Equal(0, MigratingJsonProfile.Current.LoadedProfileVersion);
            await MigratingJsonProfile.FlushAsync();
            var persisted = await File.ReadAllTextAsync(path);
            Assert.Contains("\"$xfeProfileVersion\":2", persisted);
            Assert.Contains("InstanceDisplayName", persisted);
            Assert.DoesNotContain("InstanceLegacyName", persisted);
        }
        finally
        {
            MigratingJsonProfile.DeleteProfile();
            DeleteDirectory(directory);
        }
    }

    [Fact]
    public void ValidationFailureRejectsImportAndKeepsCurrentInstance()
    {
        MigratingJsonProfile.Current = new MigratingJsonProfile();
        MigratingJsonProfile.Current.InstanceDisplayName = "accepted";
        var original = MigratingJsonProfile.Current;

        var exception = Assert.Throws<ProfileValidationException>(() =>
            MigratingJsonProfile.ImportProfile("{\"$xfeProfileVersion\":2,\"InstanceDisplayName\":\"\"}"));

        Assert.Same(original, MigratingJsonProfile.Current);
        Assert.Equal("accepted", MigratingJsonProfile.DisplayName);
        Assert.Contains("DisplayName", exception.ValidationError);
    }

    [Fact]
    public async Task ValidationFailureOnLoadKeepsCurrentAndOriginalFile()
    {
        var directory = CreateTestDirectory();
        ProfileLoadFailedEventArgs? observed = null;
        EventHandler<ProfileLoadFailedEventArgs> handler = (_, args) => observed = args;
        XFEProfile.ProfileLoadFailed += handler;
        try
        {
            MigratingJsonProfile.Current = new MigratingJsonProfile();
            MigratingJsonProfile.Current.InstanceDisplayName = "accepted";
            MigratingJsonProfile.ProfilePath = Path.Combine(directory, "invalid");
            var path = MigratingJsonProfile.ProfilePath + ".json";
            await File.WriteAllTextAsync(path, "{\"$xfeProfileVersion\":2,\"InstanceDisplayName\":\"\",\"InstanceRevision\":7}");
            var original = MigratingJsonProfile.Current;

            MigratingJsonProfile.LoadProfile();

            Assert.Same(original, MigratingJsonProfile.Current);
            Assert.Equal("accepted", MigratingJsonProfile.DisplayName);
            Assert.IsType<ProfileValidationException>(original.LastLoadException);
            Assert.True(File.Exists(path));
            Assert.NotNull(observed);
            Assert.Null(observed!.BackupPath);
        }
        finally
        {
            XFEProfile.ProfileLoadFailed -= handler;
            MigratingJsonProfile.DeleteProfile();
            DeleteDirectory(directory);
        }
    }

    [Fact]
    public async Task MigrationFailureKeepsFileAndCurrentDefaults()
    {
        var directory = CreateTestDirectory();
        try
        {
            MigratingJsonProfile.Current = new MigratingJsonProfile();
            MigratingJsonProfile.ProfilePath = Path.Combine(directory, "future");
            var path = MigratingJsonProfile.ProfilePath + ".json";
            await File.WriteAllTextAsync(path, "{\"$xfeProfileVersion\":99,\"InstanceDisplayName\":\"future\"}");

            MigratingJsonProfile.LoadProfile();

            Assert.Equal("default", MigratingJsonProfile.DisplayName);
            Assert.IsType<ProfileMigrationException>(MigratingJsonProfile.Current.LastLoadException);
            Assert.True(File.Exists(path));
            Assert.Empty(Directory.GetFiles(directory, "*.corrupt-*"));
        }
        finally
        {
            MigratingJsonProfile.DeleteProfile();
            DeleteDirectory(directory);
        }
    }

    [Fact]
    public void AllBuiltInFormatsEmitVersionMetadata()
    {
        VersionedXfeProfile.Current.InstanceValue = "xfe";
        VersionedXmlProfile.Current.InstanceValue = "xml";
        MigratingJsonProfile.Current.InstanceDisplayName = "json";

        var xfe = VersionedXfeProfile.ExportProfile();
        var xml = VersionedXmlProfile.ExportProfile();
        var json = MigratingJsonProfile.ExportProfile();
        Assert.Contains("$xfeProfileVersion", xfe);
        Assert.Contains("xfeProfileVersion=\"1\"", xml);
        Assert.DoesNotContain("<ProfileSchemaVersion>", xml);
        Assert.Contains("\"$xfeProfileVersion\":2", json);
        Assert.DoesNotContain("\"ProfileSchemaVersion\"", json);

        VersionedXfeProfile.Current.InstanceValue = "changed";
        VersionedXmlProfile.Current.InstanceValue = "changed";
        VersionedXfeProfile.ImportProfile(xfe);
        VersionedXmlProfile.ImportProfile(xml);
        Assert.Equal("xfe", VersionedXfeProfile.Value);
        Assert.Equal("xml", VersionedXmlProfile.Value);
    }

    [Fact]
    public void JsonOptionsApplyNamingPolicyAndConvertersOnRoundTrip()
    {
        JsonOptionsProfile.Current.InstanceTheme = ProfileTheme.Dark;

        var content = JsonOptionsProfile.ExportProfile();
        JsonOptionsProfile.Current.InstanceTheme = ProfileTheme.Light;
        JsonOptionsProfile.ImportProfile(content);

        Assert.Contains("\"instanceTheme\": \"dark\"", content);
        Assert.Contains(Environment.NewLine, content);
        Assert.Equal(ProfileTheme.Dark, JsonOptionsProfile.Theme);

        JsonOptionsProfile.ImportProfile("{/* supported comment */\"instanceTheme\":\"light\",}");
        Assert.Equal(ProfileTheme.Light, JsonOptionsProfile.Theme);
    }

    [Fact]
    public async Task ProfileStoreMaintainsIndependentInstancesForSameType()
    {
        var directory = CreateTestDirectory();
        try
        {
            var firstPath = Path.Combine(directory, "tenant-a.json");
            var secondPath = Path.Combine(directory, "tenant-b.json");
            var first = new ProfileStore<TenantProfile>(firstPath, autoLoad: false);
            var second = new ProfileStore<TenantProfile>(secondPath, autoLoad: false);

            first.Update(profile =>
            {
                profile.InstanceName = "A";
                profile.InstanceValues.Add("one");
            });
            second.Update(profile =>
            {
                profile.InstanceName = "B";
                profile.InstanceValues.Add("two");
            });
            await Task.WhenAll(first.SaveAsync(), second.SaveAsync());

            var reloadedFirst = new ProfileStore<TenantProfile>(firstPath);
            var reloadedSecond = new ProfileStore<TenantProfile>(secondPath);
            Assert.NotSame(reloadedFirst.Current, reloadedSecond.Current);
            Assert.Equal("A", reloadedFirst.Read(profile => profile.InstanceName));
            Assert.Equal("B", reloadedSecond.Read(profile => profile.InstanceName));
            Assert.Equal(["one"], reloadedFirst.Read(profile => profile.InstanceValues.ToArray()));
            Assert.Equal(["two"], reloadedSecond.Read(profile => profile.InstanceValues.ToArray()));
            Assert.Same(reloadedFirst.Current, reloadedFirst.Current.InstanceValues.CurrentProfile);
            Assert.Same(reloadedSecond.Current, reloadedSecond.Current.InstanceValues.CurrentProfile);
        }
        finally
        {
            DeleteDirectory(directory);
        }
    }

    [Fact]
    public void GeneratorAutomaticallyBindsProfileCollections()
    {
        AutoBindingProfile.Current = new AutoBindingProfile();

        Assert.Same(AutoBindingProfile.Current, AutoBindingProfile.Numbers.CurrentProfile);
        Assert.Same(AutoBindingProfile.Current, AutoBindingProfile.Lookup.CurrentProfile);

        AutoBindingProfile.Current.InstanceNumbers = [1, 2];
        Assert.Same(AutoBindingProfile.Current, AutoBindingProfile.Numbers.CurrentProfile);
    }

    private static string CreateTestDirectory()
    {
        var directory = Path.Combine(Path.GetTempPath(), "XFEAutoConfigTests", Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(directory);
        return directory;
    }

    private static void DeleteDirectory(string directory)
    {
        if (Directory.Exists(directory))
            Directory.Delete(directory, true);
    }
}