mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-11 01:03:51 +08:00
- 将便携版更新资产切换为普通 ZIP,并兼容复用已下载包 - 解压后映射版本化目标文件名,保留旧版 EXE 更新兼容 - 将内嵌 PowerShell 更新脚本策略收紧为 RemoteSigned - 补充 ZIP、目标路径与启动参数回归测试
141 lines
6.0 KiB
Go
141 lines
6.0 KiB
Go
package app
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestBuildWindowsMSIUpdatePowerShellScriptInstallsRelaunchesAndCleans(t *testing.T) {
|
|
script := buildWindowsMSIUpdatePowerShellScript()
|
|
mustContain := []string{
|
|
`function Save-GoNaviDesktopShortcutState`,
|
|
`function Remove-GoNaviDesktopShortcutsForTarget`,
|
|
`function Restore-GoNaviDesktopShortcutState`,
|
|
`function Send-ShellItemUpdatedNotification`,
|
|
`SHChangeNotify`,
|
|
`function Repair-LegacyGoNaviTaskbarPins`,
|
|
`while (Get-Process -Id $HostProcessId -ErrorAction SilentlyContinue)`,
|
|
`$DesktopShortcutState = Save-GoNaviDesktopShortcutState -TargetPath $Target -BackupDirectory $StagedDir`,
|
|
`if (-not $DesktopShortcutState.Succeeded)`,
|
|
`$DesktopShortcutInstallValue = $DesktopShortcutState.InstallValue`,
|
|
`Start-Process -FilePath $MSIExecPath -Verb RunAs`,
|
|
`'INSTALLFOLDER=' + (Quote-NativeArgument $TargetDir)`,
|
|
`'INSTALLDESKTOPSHORTCUT=' + $DesktopShortcutInstallValue`,
|
|
`'/passive'`,
|
|
`'/norestart'`,
|
|
`'/L*v'`,
|
|
`$InstallerExitCode -notin @(0, 1641, 3010)`,
|
|
`if (-not (Restore-GoNaviDesktopShortcutState -State $DesktopShortcutState -OnlyForeign))`,
|
|
`Repair-LegacyGoNaviTaskbarPins -TargetPath $Target`,
|
|
`function Release-UpdateMaintenanceLock`,
|
|
`[Threading.EventWaitHandle]::OpenExisting($MaintenanceEventName)`,
|
|
`[void]$HandoffEvent.Set()`,
|
|
`update maintenance lock could not be released before relaunch`,
|
|
`Start-Process -FilePath $Target -WorkingDirectory $TargetDir`,
|
|
`Remove-UpdateArtifact $Source`,
|
|
`MSI package retained for manual install`,
|
|
`previous application relaunched after MSI failure`,
|
|
}
|
|
for _, token := range mustContain {
|
|
if !strings.Contains(script, token) {
|
|
t.Fatalf("MSI updater missing %q\n%s", token, script)
|
|
}
|
|
}
|
|
if strings.Index(script, `Remove-UpdateArtifact $Source`) < strings.Index(script, `Start-Process -FilePath $Target -WorkingDirectory $TargetDir`) {
|
|
t.Fatalf("MSI package must be removed only after relaunch\n%s", script)
|
|
}
|
|
desktopStateIndex := strings.Index(script, `$DesktopShortcutState = Save-GoNaviDesktopShortcutState -TargetPath $Target -BackupDirectory $StagedDir`)
|
|
installerIndex := strings.Index(script, `Start-Process -FilePath $MSIExecPath -Verb RunAs`)
|
|
if desktopStateIndex < 0 || desktopStateIndex > installerIndex {
|
|
t.Fatalf("desktop shortcut state must be captured before MSI starts\n%s", script)
|
|
}
|
|
repairIndex := strings.Index(script, `Repair-LegacyGoNaviTaskbarPins -TargetPath $Target`)
|
|
releaseIndex := strings.Index(script, `if (-not (Release-UpdateMaintenanceLock))`)
|
|
relaunchIndex := strings.Index(script, `Start-Process -FilePath $Target -WorkingDirectory $TargetDir`)
|
|
if repairIndex < installerIndex || repairIndex > relaunchIndex {
|
|
t.Fatalf("legacy taskbar pins must be repaired after install and before relaunch\n%s", script)
|
|
}
|
|
if releaseIndex < repairIndex || releaseIndex > relaunchIndex {
|
|
t.Fatalf("maintenance lock must be released after install repair and before relaunch\n%s", script)
|
|
}
|
|
for _, r := range script {
|
|
if r > 0x7f {
|
|
t.Fatalf("MSI updater must remain ASCII-only, found %q", r)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBuildWindowsMSILaunchCommandPreservesPathsInEnvironment(t *testing.T) {
|
|
context := windowsMSIUpdateLaunchContext{
|
|
SourcePath: `C:\Users\tester\AppData\Local\GoNavi 100%\GoNavi-Installer.msi`,
|
|
TargetPath: `D:\software ! 100% & portable\GoNavi.exe`,
|
|
StagedDir: `C:\Users\tester\AppData\Local\GoNavi 100%\stage`,
|
|
LogPath: `C:\Users\tester\AppData\Local\GoNavi 100%\stage\update.log`,
|
|
MSILogPath: `C:\Users\tester\AppData\Local\GoNavi 100%\stage\msi.log`,
|
|
MSIExecPath: `C:\Windows\System32\msiexec.exe`,
|
|
MaintenanceEventName: `Global\GoNavi-Update-Test`,
|
|
HandoffEventName: `Local\GoNavi-Update-Handoff-Test`,
|
|
PID: 12345,
|
|
}
|
|
cmd := buildWindowsMSILaunchCommand(filepath.Join(context.StagedDir, "update-msi.ps1"), context)
|
|
wantArgs := []string{
|
|
"powershell.exe",
|
|
"-NoProfile",
|
|
"-NonInteractive",
|
|
"-ExecutionPolicy",
|
|
"RemoteSigned",
|
|
"-File",
|
|
filepath.Join(context.StagedDir, "update-msi.ps1"),
|
|
}
|
|
if len(cmd.Args) != len(wantArgs) {
|
|
t.Fatalf("unexpected arg length: got %d want %d, args=%v", len(cmd.Args), len(wantArgs), cmd.Args)
|
|
}
|
|
for index := range wantArgs {
|
|
if cmd.Args[index] != wantArgs[index] {
|
|
t.Fatalf("unexpected arg[%d]: got %q want %q", index, cmd.Args[index], wantArgs[index])
|
|
}
|
|
}
|
|
want := map[string]string{
|
|
"GONAVI_UPDATE_SOURCE": context.SourcePath,
|
|
"GONAVI_UPDATE_TARGET": context.TargetPath,
|
|
"GONAVI_UPDATE_STAGED_DIR": context.StagedDir,
|
|
"GONAVI_UPDATE_LOG_PATH": context.LogPath,
|
|
"GONAVI_UPDATE_MSI_LOG_PATH": context.MSILogPath,
|
|
"GONAVI_UPDATE_MSIEXEC_PATH": context.MSIExecPath,
|
|
"GONAVI_UPDATE_MAINTENANCE_EVENT_NAME": context.MaintenanceEventName,
|
|
"GONAVI_UPDATE_HANDOFF_EVENT_NAME": context.HandoffEventName,
|
|
"GONAVI_UPDATE_PID": "12345",
|
|
}
|
|
got := make(map[string]string, len(want))
|
|
for _, item := range cmd.Env {
|
|
name, value, ok := strings.Cut(item, "=")
|
|
if ok {
|
|
if _, exists := want[name]; exists {
|
|
got[name] = value
|
|
}
|
|
}
|
|
}
|
|
for name, value := range want {
|
|
if got[name] != value {
|
|
t.Fatalf("environment %s = %q, want %q", name, got[name], value)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResolveWindowsMSIExecPathPrefersExplicitOverride(t *testing.T) {
|
|
values := map[string]string{
|
|
"GONAVI_UPDATE_MSIEXEC_PATH": `D:\\tools\\fake-msiexec.exe`,
|
|
"SystemRoot": `C:\\Windows`,
|
|
}
|
|
got := resolveWindowsMSIExecPath(func(name string) string { return values[name] })
|
|
if got != values["GONAVI_UPDATE_MSIEXEC_PATH"] {
|
|
t.Fatalf("msiexec path = %q, want override %q", got, values["GONAVI_UPDATE_MSIEXEC_PATH"])
|
|
}
|
|
delete(values, "GONAVI_UPDATE_MSIEXEC_PATH")
|
|
wantSystem := filepath.Join(values["SystemRoot"], "System32", "msiexec.exe")
|
|
if got := resolveWindowsMSIExecPath(func(name string) string { return values[name] }); got != wantSystem {
|
|
t.Fatalf("msiexec path = %q, want SystemRoot path %q", got, wantSystem)
|
|
}
|
|
}
|