Files
MyGoNavi/internal/app/methods_update_windows_script_test.go
Syngnat e1fb5b12d4 🐛 fix(update): 修复 Windows 在线更新中文路径失效
- 将 Windows 更新脚本统一为 PowerShell,并通过 Unicode 环境变量传递中文与特殊字符路径
- 保留当前可执行文件路径,补充替换重试、启动健康检查、失败回滚和严格 ZIP 选择
- 更新前复用未保存 SQL 确认流程,并延长退出兜底时间
- 新增 Windows 原生集成测试并接入 dev/release 构建
2026-07-10 13:31:34 +08:00

139 lines
4.5 KiB
Go

package app
import (
"strings"
"testing"
)
func TestBuildWindowsPowerShellScriptUsesLiteralPathOperations(t *testing.T) {
script := buildWindowsPowerShellScript()
mustContain := []string{
`$Source = $env:GONAVI_UPDATE_SOURCE`,
`$Target = $env:GONAVI_UPDATE_TARGET`,
`Test-Path -LiteralPath $Source -PathType Leaf`,
`Expand-Archive -LiteralPath $Source -DestinationPath $ExtractDir -Force`,
`Move-Item -LiteralPath $Target -Destination $TargetOld -Force`,
`Copy-Item -LiteralPath $SourceExe -Destination $Target -Force`,
`Start-Process -FilePath $Target -WorkingDirectory $TargetDir`,
}
for _, want := range mustContain {
if !strings.Contains(script, want) {
t.Fatalf("Windows PowerShell updater missing required token: %s\n%s", want, script)
}
}
}
func TestBuildWindowsPowerShellScriptWaitsRetriesAndRollsBack(t *testing.T) {
script := buildWindowsPowerShellScript()
mustContain := []string{
`while (Get-Process -Id $HostProcessId -ErrorAction SilentlyContinue)`,
`$waitedSeconds -ge 90`,
`Start-Sleep -Seconds 3`,
`for ($attempt = 0; $attempt -lt 15; $attempt++)`,
`if ($PreviousTargetBackedUp -or $TargetWriteStarted)`,
`previous executable backup is missing`,
`Restore-PreviousTarget`,
`if ($NewProcess.HasExited)`,
`package kept for manual install`,
`previous application relaunched after update failure`,
}
for _, want := range mustContain {
if !strings.Contains(script, want) {
t.Fatalf("Windows PowerShell updater missing reliability token: %s\n%s", want, script)
}
}
}
func TestBuildWindowsPowerShellScriptSelectsPortableExecutableByStrictPriority(t *testing.T) {
script := buildWindowsPowerShellScript()
priorityTokens := []string{
`$TargetFileName = [IO.Path]::GetFileName($TargetPath)`,
`$ExactTargetMatches = @($ExecutableCandidates | Where-Object`,
`$PackageExecutableName = [IO.Path]::GetFileNameWithoutExtension($PackagePath) + '.exe'`,
`$PackageNameMatches = @($ExecutableCandidates | Where-Object`,
`$GoNaviMatches = @($ExecutableCandidates | Where-Object`,
`if ($ExecutableCandidates.Count -eq 1)`,
`throw ("ambiguous portable zip: found "`,
}
lastIndex := -1
for _, token := range priorityTokens {
index := strings.Index(script, token)
if index < 0 {
t.Fatalf("Windows PowerShell updater missing strict ZIP selection token %q\n%s", token, script)
}
if index <= lastIndex {
t.Fatalf("Windows PowerShell ZIP selection token %q is out of priority order\n%s", token, script)
}
lastIndex = index
}
if strings.Contains(script, `Select-Object -First 1`) {
t.Fatalf("Windows PowerShell updater must not choose an arbitrary executable from a ZIP\n%s", script)
}
if !strings.Contains(script, `package retained for manual install`) {
t.Fatalf("ambiguous ZIP failure must explain that the package is retained\n%s", script)
}
}
func TestBuildWindowsPowerShellScriptUsesCRLFLineEndings(t *testing.T) {
script := buildWindowsPowerShellScript()
if !strings.Contains(script, "\r\n") {
t.Fatal("Windows PowerShell updater should use CRLF line endings")
}
if strings.Contains(script, "$ErrorActionPreference = 'Stop'\n\n") {
t.Fatal("Windows PowerShell updater should not contain LF-only line endings")
}
}
func TestBuildWindowsPowerShellScriptAvoidsCmdParsing(t *testing.T) {
script := buildWindowsPowerShellScript()
for _, bad := range []string{
`cmd.exe`,
`EnableDelayedExpansion`,
`__GONAVI_UPDATE_`,
`-FilePath '%TARGET%'`,
} {
if strings.Contains(script, bad) {
t.Fatalf("Windows PowerShell updater must not contain legacy cmd token %q\n%s", bad, script)
}
}
for _, r := range script {
if r > 0x7f {
t.Fatalf("Windows PowerShell updater must remain ASCII-only, found %q", r)
}
}
}
func TestBuildWindowsLaunchCommandUsesHiddenPowerShellFile(t *testing.T) {
context := windowsUpdateLaunchContext{
SourcePath: `C:\tmp\GoNavi-0.8.5-Windows-Amd64.exe`,
TargetPath: `C:\GoNavi\GoNavi.exe`,
CurrentTargetPath: `C:\GoNavi\GoNavi.exe`,
StagedDir: `C:\tmp\gonavi-update`,
LogPath: `C:\tmp\gonavi-update\update.log`,
PID: 12345,
}
scriptPath := `C:\tmp\gonavi-update\update.ps1`
cmd := buildWindowsLaunchCommand(scriptPath, context)
want := []string{
"powershell.exe",
"-NoProfile",
"-NonInteractive",
"-ExecutionPolicy",
"Bypass",
"-File",
scriptPath,
}
if len(cmd.Args) != len(want) {
t.Fatalf("unexpected arg length: got %d want %d, args=%v", len(cmd.Args), len(want), cmd.Args)
}
for i := range want {
if cmd.Args[i] != want[i] {
t.Fatalf("unexpected arg[%d]: got %q want %q", i, cmd.Args[i], want[i])
}
}
}