LANCommander/LANCommander.SDK.Tests/Helpers/ScriptHelperTests.cs
2026-02-23 19:03:18 -06:00

272 lines
9.7 KiB
C#

using LANCommander.SDK.Enums;
using LANCommander.SDK.Helpers;
using LANCommander.SDK.Models;
namespace LANCommander.SDK.Tests.Helpers;
public class ScriptHelperTests : IDisposable
{
private readonly string _tempDir;
public ScriptHelperTests()
{
_tempDir = Path.Combine(Path.GetTempPath(), $"lc-script-tests-{Guid.NewGuid()}");
Directory.CreateDirectory(_tempDir);
}
public void Dispose()
{
if (Directory.Exists(_tempDir))
Directory.Delete(_tempDir, true);
}
private Game MakeGame(params Script[] scripts) => new()
{
Id = Guid.NewGuid(),
Title = "Test Game",
InstallDirectory = _tempDir,
Scripts = scripts
};
private static Script MakeScript(ScriptType type, string contents = "Write-Host 'test'", bool requiresAdmin = false) => new()
{
Type = type,
Name = type.ToString(),
Contents = contents,
RequiresAdmin = requiresAdmin
};
// ── GetScriptFileName ─────────────────────────────────────────────────────
[Theory]
[InlineData(ScriptType.Install, "Install.ps1")]
[InlineData(ScriptType.Uninstall, "Uninstall.ps1")]
[InlineData(ScriptType.NameChange, "ChangeName.ps1")]
[InlineData(ScriptType.KeyChange, "ChangeKey.ps1")]
[InlineData(ScriptType.DetectInstall, "DetectInstall.ps1")]
[InlineData(ScriptType.BeforeStart, "BeforeStart.ps1")]
[InlineData(ScriptType.AfterStop, "AfterStop.ps1")]
public void GetScriptFileName_ReturnsExpectedFilename(ScriptType type, string expected)
{
var result = ScriptHelper.GetScriptFileName(type);
Assert.Equal(expected, result);
}
// ── GetScriptFilePath ─────────────────────────────────────────────────────
[Fact]
public void GetScriptFilePath_WithGuidId_ReturnsPathUnderLanCommanderMetadata()
{
var id = Guid.NewGuid();
var result = ScriptHelper.GetScriptFilePath(_tempDir, id, ScriptType.Install);
var expected = Path.Combine(_tempDir, ".lancommander", id.ToString(), "Install.ps1");
Assert.Equal(expected, result);
}
[Fact]
public void GetScriptFilePath_WithStringId_ReturnsCorrectPath()
{
var result = ScriptHelper.GetScriptFilePath(_tempDir, "my-tool-id", ScriptType.BeforeStart);
var expected = Path.Combine(_tempDir, ".lancommander", "my-tool-id", "BeforeStart.ps1");
Assert.Equal(expected, result);
}
[Theory]
[InlineData(ScriptType.Install, "Install.ps1")]
[InlineData(ScriptType.Uninstall, "Uninstall.ps1")]
[InlineData(ScriptType.AfterStop, "AfterStop.ps1")]
public void GetScriptFilePath_FilenameMatchesGetScriptFileName(ScriptType type, string expectedFile)
{
var result = ScriptHelper.GetScriptFilePath(_tempDir, Guid.NewGuid(), type);
Assert.Equal(expectedFile, Path.GetFileName(result));
}
// ── GetScriptContents (Game) ──────────────────────────────────────────────
[Fact]
public void GetScriptContents_Game_WhenNoScripts_ReturnsEmpty()
{
var game = MakeGame();
var result = ScriptHelper.GetScriptContents(game, ScriptType.Install);
Assert.Equal(string.Empty, result);
}
[Fact]
public void GetScriptContents_Game_WhenScriptTypeNotPresent_ReturnsEmpty()
{
var game = MakeGame(MakeScript(ScriptType.Uninstall, "uninstall contents"));
var result = ScriptHelper.GetScriptContents(game, ScriptType.Install);
Assert.Equal(string.Empty, result);
}
[Fact]
public void GetScriptContents_Game_WhenScriptExists_ReturnsContents()
{
var game = MakeGame(MakeScript(ScriptType.Install, "Write-Host 'install'"));
var result = ScriptHelper.GetScriptContents(game, ScriptType.Install);
Assert.Equal("Write-Host 'install'", result);
}
[Fact]
public void GetScriptContents_Game_WhenRequiresAdmin_PrependsAdminHeader()
{
var game = MakeGame(MakeScript(ScriptType.Install, "Write-Host 'install'", requiresAdmin: true));
var result = ScriptHelper.GetScriptContents(game, ScriptType.Install);
Assert.StartsWith("# Requires Admin", result);
Assert.Contains("Write-Host 'install'", result);
}
[Fact]
public void GetScriptContents_Game_WhenDoesNotRequireAdmin_DoesNotPrependHeader()
{
var game = MakeGame(MakeScript(ScriptType.Install, "Write-Host 'install'", requiresAdmin: false));
var result = ScriptHelper.GetScriptContents(game, ScriptType.Install);
Assert.DoesNotContain("# Requires Admin", result);
}
[Fact]
public void GetScriptContents_Game_WhenMultipleScripts_ReturnsCorrectOne()
{
var game = MakeGame(
MakeScript(ScriptType.Install, "install contents"),
MakeScript(ScriptType.Uninstall, "uninstall contents"));
Assert.Equal("install contents", ScriptHelper.GetScriptContents(game, ScriptType.Install));
Assert.Equal("uninstall contents", ScriptHelper.GetScriptContents(game, ScriptType.Uninstall));
}
// ── SaveTempScriptAsync (string) ──────────────────────────────────────────
[Fact]
public async Task SaveTempScriptAsync_String_CreatesPs1File()
{
var tempPath = await ScriptHelper.SaveTempScriptAsync("Write-Host 'test'");
try
{
Assert.True(File.Exists(tempPath));
Assert.EndsWith(".ps1", tempPath);
}
finally
{
if (File.Exists(tempPath)) File.Delete(tempPath);
}
}
[Fact]
public async Task SaveTempScriptAsync_String_WritesExpectedContent()
{
var contents = "Write-Host 'hello world'";
var tempPath = await ScriptHelper.SaveTempScriptAsync(contents);
try
{
Assert.Equal(contents, await File.ReadAllTextAsync(tempPath));
}
finally
{
if (File.Exists(tempPath)) File.Delete(tempPath);
}
}
[Fact]
public async Task SaveTempScriptAsync_String_EachCallCreatesDistinctFile()
{
var path1 = await ScriptHelper.SaveTempScriptAsync("script 1");
var path2 = await ScriptHelper.SaveTempScriptAsync("script 2");
try
{
Assert.NotEqual(path1, path2);
}
finally
{
if (File.Exists(path1)) File.Delete(path1);
if (File.Exists(path2)) File.Delete(path2);
}
}
// ── SaveTempScriptAsync (Script model) ────────────────────────────────────
[Fact]
public async Task SaveTempScriptAsync_Script_CreatesPs1FileWithContents()
{
var script = MakeScript(ScriptType.Install, "Write-Host 'from model'");
var tempPath = await ScriptHelper.SaveTempScriptAsync(script);
try
{
Assert.True(File.Exists(tempPath));
Assert.EndsWith(".ps1", tempPath);
Assert.Equal(script.Contents, await File.ReadAllTextAsync(tempPath));
}
finally
{
if (File.Exists(tempPath)) File.Delete(tempPath);
}
}
// ── SaveScriptAsync (Game) ────────────────────────────────────────────────
[Fact]
public async Task SaveScriptAsync_Game_WhenScriptExists_WritesFileAtExpectedPath()
{
var script = MakeScript(ScriptType.Install, "Write-Host 'install'");
var game = MakeGame(script);
await ScriptHelper.SaveScriptAsync(game, ScriptType.Install, _tempDir);
var expectedPath = ScriptHelper.GetScriptFilePath(_tempDir, game.Id, ScriptType.Install);
Assert.True(File.Exists(expectedPath));
Assert.Equal("Write-Host 'install'", await File.ReadAllTextAsync(expectedPath));
}
[Fact]
public async Task SaveScriptAsync_Game_WhenScriptDoesNotExist_DoesNotCreateFile()
{
var game = MakeGame(); // no scripts
await ScriptHelper.SaveScriptAsync(game, ScriptType.Install, _tempDir);
var path = ScriptHelper.GetScriptFilePath(_tempDir, game.Id, ScriptType.Install);
Assert.False(File.Exists(path));
}
[Fact]
public async Task SaveScriptAsync_Game_CreatesParentDirectoriesAsNeeded()
{
var script = MakeScript(ScriptType.Uninstall, "Write-Host 'uninstall'");
var game = MakeGame(script);
var nestedInstallDir = Path.Combine(_tempDir, "nested", "install");
await ScriptHelper.SaveScriptAsync(game, ScriptType.Uninstall, nestedInstallDir);
var expectedPath = ScriptHelper.GetScriptFilePath(nestedInstallDir, game.Id, ScriptType.Uninstall);
Assert.True(File.Exists(expectedPath));
}
[Fact]
public async Task SaveScriptAsync_Game_OverwritesExistingFile()
{
var game = MakeGame(MakeScript(ScriptType.Install, "first"));
await ScriptHelper.SaveScriptAsync(game, ScriptType.Install, _tempDir);
// Replace the script on the game object and save again
game.Scripts = new[] { MakeScript(ScriptType.Install, "second") };
await ScriptHelper.SaveScriptAsync(game, ScriptType.Install, _tempDir);
var path = ScriptHelper.GetScriptFilePath(_tempDir, game.Id, ScriptType.Install);
Assert.Equal("second", await File.ReadAllTextAsync(path));
}
}