Files
SilverMetal/windows/welcome/tests/SilverOS.Welcome.Tests/ApplyServicesTests.cs
sysadmin bf21eababe
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 4m32s
fix(welcome): make bootstrap teardown best-effort (LogonCount=1 already disables auto-logon; cleanup must not fail the apply)
2026-06-09 12:15:56 +01:00

97 lines
4.5 KiB
C#

using Moq;
using SilverOS.Welcome.Core.Apply;
public class ApplyServicesTests
{
private static Mock<IProcessRunner> Ok()
{
var m = new Mock<IProcessRunner>();
m.Setup(r => r.RunAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new ProcessResult(0, "", ""));
return m;
}
private static Mock<IProcessRunner> Fail()
{
var m = new Mock<IProcessRunner>();
m.Setup(r => r.RunAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new ProcessResult(1, "", "the operation failed"));
return m;
}
[Fact]
public async Task AccountService_throws_on_nonzero_exit()
{
await Assert.ThrowsAsync<InvalidOperationException>(() =>
new AccountService(Fail().Object).CreateAccountsAsync("alice", "pw", "apw"));
}
[Fact]
public async Task BitLockerService_throws_on_nonzero_exit()
{
await Assert.ThrowsAsync<InvalidOperationException>(() =>
new BitLockerService(Fail().Object).EnableAsync("123456"));
}
// Note: BootstrapService is intentionally best-effort (teardown cleanups must not fail the
// apply — auto-logon is already neutralised by the answer file's LogonCount=1), so it does
// NOT throw on a non-zero exit.
[Fact]
public async Task AccountService_creates_standard_daily_and_admin()
{
var run = Ok();
await new AccountService(run.Object).CreateAccountsAsync("alice", "pw1", "adminpw");
// daily user is a Standard user (added to Users, NOT Administrators)
run.Verify(r => r.RunAsync("powershell.exe", It.Is<string>(s =>
s.Contains("New-LocalUser") && s.Contains("alice")), It.IsAny<CancellationToken>()));
// negative: the daily-user New-LocalUser call must never mention Administrators
run.Verify(r => r.RunAsync("powershell.exe", It.Is<string>(s =>
s.Contains("New-LocalUser") && s.Contains("alice") && !s.Contains("Administrators")),
It.IsAny<CancellationToken>()), Times.Once);
run.Verify(r => r.RunAsync("powershell.exe", It.Is<string>(s =>
s.Contains("'SilverOS Admin'") && s.Contains("Administrators")), It.IsAny<CancellationToken>()));
}
[Fact]
public async Task BitLockerService_enables_tpm_and_pin()
{
var run = Ok();
await new BitLockerService(run.Object).EnableAsync("123456");
run.Verify(r => r.RunAsync("powershell.exe", It.Is<string>(s =>
s.Contains("Enable-BitLocker") && s.Contains("TpmAndPinProtector")), It.IsAny<CancellationToken>()));
}
[Fact]
public async Task BitLockerService_sets_fve_pin_policy_and_strips_tpm_only_protector()
{
var run = Ok();
await new BitLockerService(run.Object).EnableAsync("123456");
// Sets the FVE "require additional authentication at startup" policy so the
// TPM+PIN protector actually applies (otherwise it silently degrades to TPM-only).
run.Verify(r => r.RunAsync("powershell.exe", It.Is<string>(s =>
s.Contains("UseAdvancedStartup") && s.Contains("UseTPMPIN")), It.IsAny<CancellationToken>()));
// Handles a volume already encrypted by Windows auto-device-encryption (TPM-only)
// by adding the TPM+PIN protector instead of failing.
run.Verify(r => r.RunAsync("powershell.exe", It.Is<string>(s =>
s.Contains("Add-BitLockerKeyProtector")), It.IsAny<CancellationToken>()));
// Removes any TPM-only protector so the device requires the PIN at pre-boot.
run.Verify(r => r.RunAsync("powershell.exe", It.Is<string>(s =>
s.Contains("Remove-BitLockerKeyProtector")), It.IsAny<CancellationToken>()));
// Ejects optical install media first (BitLocker refuses to enroll with bootable media present).
run.Verify(r => r.RunAsync("powershell.exe", It.Is<string>(s =>
s.Contains("Shell.Application") && s.Contains("Eject")), It.IsAny<CancellationToken>()));
}
[Fact]
public async Task BootstrapService_removes_autologon_and_account()
{
var run = Ok();
await new BootstrapService(run.Object).TearDownAsync("sm-bootstrap");
run.Verify(r => r.RunAsync("powershell.exe", It.Is<string>(s =>
s.Contains("AutoAdminLogon") && s.Contains("0")), It.IsAny<CancellationToken>()));
run.Verify(r => r.RunAsync("powershell.exe", It.Is<string>(s =>
s.Contains("Remove-LocalUser") && s.Contains("sm-bootstrap")), It.IsAny<CancellationToken>()));
}
}