$ErrorActionPreference = 'Stop' $project = Split-Path $PSScriptRoot -Parent $package = Join-Path $project 'dist\MusicBridge-win-x64' $testRoot = [IO.Path]::GetFullPath((Join-Path $project ('dist\install-smoke-' + [guid]::NewGuid().ToString('N')))) $allowed = [IO.Path]::GetFullPath((Join-Path $project 'dist')) + '\' if (!$testRoot.StartsWith($allowed, [StringComparison]::OrdinalIgnoreCase)) { throw 'Unsafe test path.' } & (Join-Path $package 'Install.ps1') -InstallDirectory $testRoot -NoShortcut $exe = Join-Path $testRoot 'MusicBridge.Agent.exe' if (!(Test-Path -LiteralPath (Join-Path $testRoot 'coreclr.dll'))) { throw 'Self-contained runtime is missing.' } $config = Get-Content -LiteralPath (Join-Path $testRoot 'MusicBridge.Agent.runtimeconfig.json') -Raw | ConvertFrom-Json if (!$config.runtimeOptions.includedFrameworks) { throw 'Package unexpectedly depends on globally installed runtimes.' } & $exe --help if ($LASTEXITCODE -ne 0) { throw 'Installed executable failed to start.' } & (Join-Path $package 'Install.ps1') -InstallDirectory $testRoot -NoShortcut $probe = [IO.File]::Open($exe, 'Open', 'Read', 'None') try { $blocked = $false try { & (Join-Path $package 'Install.ps1') -InstallDirectory $testRoot -NoShortcut } catch { $blocked = $true } if (!$blocked) { throw 'Installer did not reject locked executable.' } } finally { $probe.Dispose() } & (Join-Path $testRoot 'Uninstall.ps1') if (Test-Path -LiteralPath $testRoot) { throw 'Uninstall did not remove test directory.' } Write-Host 'PASS: install, self-contained launch, update, locked-file protection, uninstall.'