mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
174 lines
6.3 KiB
C#
174 lines
6.3 KiB
C#
using Xunit;
|
|
|
|
namespace LanMountainDesktop.Tests;
|
|
|
|
public sealed class LauncherArchitectureTests
|
|
{
|
|
[Fact]
|
|
public void CoreLauncherFolders_DoNotUseAvaloniaNamespaces()
|
|
{
|
|
var forbidden = new[] { "Deployment", "Update", "Startup", "Infrastructure" };
|
|
foreach (var folder in forbidden.Select(folder => Path.Combine(LauncherProjectRoot, folder)))
|
|
{
|
|
var offenders = Directory
|
|
.EnumerateFiles(folder, "*.cs", SearchOption.AllDirectories)
|
|
.Where(file => File.ReadAllText(file).Contains("using Avalonia", StringComparison.Ordinal))
|
|
.Select(RelativeToRepo)
|
|
.ToArray();
|
|
|
|
Assert.Empty(offenders);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void LauncherFlowCoordinator_TypeDoesNotExist()
|
|
{
|
|
var coordinator = typeof(LanMountainDesktop.Launcher.Shell.LauncherOrchestrator).Assembly
|
|
.GetType("LanMountainDesktop.Launcher.Services.LauncherFlowCoordinator", throwOnError: false);
|
|
Assert.Null(coordinator);
|
|
}
|
|
|
|
[Fact]
|
|
public void CliAndShellEntryHandlers_DoNotDependOnConcreteUpdateEngineFacade()
|
|
{
|
|
var guardedFiles = new[]
|
|
{
|
|
Path.Combine(LauncherProjectRoot, "Infrastructure", "Commands.cs")
|
|
}.Concat(Directory.EnumerateFiles(
|
|
Path.Combine(LauncherProjectRoot, "Shell", "EntryHandlers"),
|
|
"*.cs",
|
|
SearchOption.AllDirectories));
|
|
|
|
var offenders = guardedFiles
|
|
.Where(file => File.ReadAllText(file).Contains("UpdateEngineFacade", StringComparison.Ordinal))
|
|
.Select(RelativeToRepo)
|
|
.ToArray();
|
|
|
|
Assert.Empty(offenders);
|
|
}
|
|
|
|
[Fact]
|
|
public void LauncherProject_DoesNotOwnUpdateApplyOrRollback()
|
|
{
|
|
var launcherFiles = Directory
|
|
.EnumerateFiles(LauncherProjectRoot, "*.cs", SearchOption.AllDirectories)
|
|
.Where(file => !file.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase))
|
|
.Where(file => !file.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase))
|
|
.ToArray();
|
|
|
|
var forbiddenTokens = new[]
|
|
{
|
|
"LauncherUpdateCommandExecutor",
|
|
"PlondsUpdateApplier",
|
|
"UpdateRollbackGateway",
|
|
"UpdateInstallGateway",
|
|
"LanMountainDesktop.Services.Update",
|
|
"apply-update",
|
|
"rollback --app-root"
|
|
};
|
|
|
|
var offenders = launcherFiles
|
|
.SelectMany(file => forbiddenTokens
|
|
.Where(token => File.ReadAllText(file).Contains(token, StringComparison.Ordinal))
|
|
.Select(token => $"{RelativeToRepo(file)} contains {token}"))
|
|
.ToArray();
|
|
|
|
Assert.Empty(offenders);
|
|
}
|
|
|
|
[Fact]
|
|
public void LauncherProjectFile_DoesNotSourceLinkHostUpdateImplementation()
|
|
{
|
|
var project = File.ReadAllText(Path.Combine(LauncherProjectRoot, "LanMountainDesktop.Launcher.csproj"));
|
|
|
|
Assert.DoesNotContain(@"..\LanMountainDesktop\Services\Update", project, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("PlondsUpdateApplier", project, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("UpdateRollbackGateway", project, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("UpdateInstallGateway", project, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void HostUpdateFlow_DoesNotDelegateApplyOrRollbackToLauncher()
|
|
{
|
|
var guardedFiles = new[]
|
|
{
|
|
Path.Combine(RepoRoot, "LanMountainDesktop", "Services", "Update", "UpdateInstallGateway.cs"),
|
|
Path.Combine(RepoRoot, "LanMountainDesktop", "Services", "Update", "UpdateOrchestrator.cs")
|
|
};
|
|
|
|
var forbiddenTokens = new[]
|
|
{
|
|
"LauncherPathResolver",
|
|
"ResolveLauncherExecutablePath",
|
|
"apply-update",
|
|
"rollback --app-root",
|
|
"Launched Launcher"
|
|
};
|
|
|
|
var offenders = guardedFiles
|
|
.SelectMany(file => forbiddenTokens
|
|
.Where(token => File.ReadAllText(file).Contains(token, StringComparison.Ordinal))
|
|
.Select(token => $"{RelativeToRepo(file)} contains {token}"))
|
|
.ToArray();
|
|
|
|
Assert.Empty(offenders);
|
|
}
|
|
|
|
[Fact]
|
|
public void HostUpdateFlow_OwnsDeltaApplyAndRollbackExecution()
|
|
{
|
|
var installGateway = File.ReadAllText(Path.Combine(
|
|
RepoRoot,
|
|
"LanMountainDesktop",
|
|
"Services",
|
|
"Update",
|
|
"UpdateInstallGateway.cs"));
|
|
var orchestrator = File.ReadAllText(Path.Combine(
|
|
RepoRoot,
|
|
"LanMountainDesktop",
|
|
"Services",
|
|
"Update",
|
|
"UpdateOrchestrator.cs"));
|
|
|
|
Assert.Contains("new PlondsUpdateApplier", installGateway, StringComparison.Ordinal);
|
|
Assert.Contains("DeploymentLockService.ClearLock", installGateway, StringComparison.Ordinal);
|
|
Assert.Contains("new UpdateRollbackGateway().RollbackLatest", orchestrator, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("LanMountainDesktop.Launcher", orchestrator, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void LauncherCompositionRootStaysThin()
|
|
{
|
|
AssertFileLineCountAtMost(Path.Combine(LauncherProjectRoot, "Shell", "LauncherCompositionRoot.cs"), 80);
|
|
}
|
|
|
|
private static string LauncherProjectRoot => Path.Combine(RepoRoot, "LanMountainDesktop.Launcher");
|
|
|
|
private static string RepoRoot
|
|
{
|
|
get
|
|
{
|
|
var current = new DirectoryInfo(AppContext.BaseDirectory);
|
|
while (current is not null)
|
|
{
|
|
if (File.Exists(Path.Combine(current.FullName, "LanMountainDesktop.slnx")))
|
|
{
|
|
return current.FullName;
|
|
}
|
|
|
|
current = current.Parent;
|
|
}
|
|
|
|
throw new InvalidOperationException("Unable to locate repository root.");
|
|
}
|
|
}
|
|
|
|
private static void AssertFileLineCountAtMost(string path, int maxLines)
|
|
{
|
|
var lineCount = File.ReadLines(path).Count();
|
|
Assert.True(lineCount <= maxLines, $"{RelativeToRepo(path)} has {lineCount} lines; expected <= {maxLines}.");
|
|
}
|
|
|
|
private static string RelativeToRepo(string path) => Path.GetRelativePath(RepoRoot, path);
|
|
}
|