$ErrorActionPreference = 'Stop'
$rootDir = Split-Path -Parent $PSScriptRoot
$testRoot = Join-Path ([System.IO.Path]::GetTempPath()) "bot-forge-installer-$PID-$([guid]::NewGuid().ToString('N'))"
$assets = Join-Path $testRoot 'assets with spaces'
$installRoot = Join-Path $testRoot 'install with spaces'
$server = $null
try {
New-Item -ItemType Directory -Force -Path $assets, $installRoot | Out-Null
$architecture = try {
[System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()
} catch {
if ($env:PROCESSOR_ARCHITEW6432) {
$env:PROCESSOR_ARCHITEW6432
} else {
$env:PROCESSOR_ARCHITECTURE
}
}
$platform = switch ($architecture) {
{ $_ -in 'X64', 'AMD64' } { 'windows-x86_64'; break }
{ $_ -in 'Arm64', 'ARM64' } { 'windows-aarch64'; break }
default { throw "Unsupported Windows test architecture: $architecture" }
}
$fakeBinary = Join-Path $assets "$platform-bot-forge.exe"
$source = @'
using System;
using System.IO;
public static class Program
{
public static int Main(string[] args)
{
File.WriteAllText(
Environment.GetEnvironmentVariable("BOT_FORGE_TEST_RESULT"),
string.Join("|", args) + Environment.NewLine +
Environment.GetEnvironmentVariable("BOT_FORGE_HOME"));
return 0;
}
}
'@
Add-Type -TypeDefinition $source -OutputAssembly $fakeBinary -OutputType ConsoleApplication
Set-Content -LiteralPath (Join-Path $assets 'bot-forge.toml') -Value 'catalog = "rust-dev"' -Encoding Ascii
$checksumLines = foreach ($name in @('bot-forge.toml', "$platform-bot-forge.exe")) {
$hash = (Get-FileHash -LiteralPath (Join-Path $assets $name) -Algorithm SHA256).Hash.ToLowerInvariant()
"$hash $name"
}
Set-Content -LiteralPath (Join-Path $assets 'SHA256SUMS') -Value $checksumLines -Encoding Ascii
$checksumsSha256 = (Get-FileHash -LiteralPath (Join-Path $assets 'SHA256SUMS') -Algorithm SHA256).Hash.ToLowerInvariant()
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
$listener.Start()
$port = ([System.Net.IPEndPoint]$listener.LocalEndpoint).Port
$listener.Stop()
$server = Start-Process python -ArgumentList @('-m', 'http.server', "$port", '--bind', '127.0.0.1') -WorkingDirectory $assets -PassThru -WindowStyle Hidden
$ready = $false
for ($attempt = 0; $attempt -lt 50; $attempt++) {
try {
$client = [System.Net.Sockets.TcpClient]::new('127.0.0.1', $port)
$client.Dispose()
$ready = $true
break
} catch {
Start-Sleep -Milliseconds 100
}
}
if (-not $ready) {
throw 'Local installer fixture server did not start.'
}
$env:BOT_FORGE_HOME = Join-Path $installRoot 'data'
$env:BOT_FORGE_BIN_DIR = Join-Path $installRoot 'managed bin'
$env:BOT_FORGE_CONFIG_DIR = Join-Path $installRoot 'config'
$env:BOT_FORGE_BASE_URL = "http://127.0.0.1:$port"
$env:BOT_FORGE_CHECKSUMS_SHA256 = $checksumsSha256
$env:BOT_FORGE_TEST_RESULT = Join-Path $installRoot 'result.txt'
$env:BOT_FORGE_CHECKSUMS_SHA256 = '0' * 64
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $rootDir 'install.ps1') standard --yes
if ($LASTEXITCODE -eq 0) {
throw 'install.ps1 accepted an untrusted SHA256SUMS manifest.'
}
$env:BOT_FORGE_CHECKSUMS_SHA256 = $checksumsSha256
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $rootDir 'install.ps1') standard --yes
if ($LASTEXITCODE -ne 0) {
throw "install.ps1 exited with $LASTEXITCODE"
}
$installedBinary = Join-Path $env:BOT_FORGE_BIN_DIR 'bot-forge.exe'
$installedConfig = Join-Path $env:BOT_FORGE_CONFIG_DIR 'bot-forge.toml'
if ((Get-FileHash $installedBinary).Hash -ne (Get-FileHash $fakeBinary).Hash) {
throw 'Installed binary differs from the verified fixture.'
}
$expectedResult = "install|--config|$installedConfig|standard|--yes$([Environment]::NewLine)$env:BOT_FORGE_HOME"
if ((Get-Content -Raw $env:BOT_FORGE_TEST_RESULT).Trim() -ne $expectedResult) {
throw 'Installer did not forward paths, arguments, or BOT_FORGE_HOME correctly.'
}
Set-Content -LiteralPath $installedConfig -Value 'catalog = "preserved"' -Encoding Ascii
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $rootDir 'install.ps1') smoke --yes
if ($LASTEXITCODE -ne 0 -or (Get-Content -Raw $installedConfig).Trim() -ne 'catalog = "preserved"') {
throw 'Installer did not preserve the existing configuration.'
}
Write-Host "Windows installer contract passed for $platform."
} finally {
if ($server) {
Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue
}
Remove-Item -LiteralPath $testRoot -Recurse -Force -ErrorAction SilentlyContinue
}