mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-19 03:41:40 +08:00
- 将 Windows 更新脚本统一为 PowerShell,并通过 Unicode 环境变量传递中文与特殊字符路径 - 保留当前可执行文件路径,补充替换重试、启动健康检查、失败回滚和严格 ZIP 选择 - 更新前复用未保存 SQL 确认流程,并延长退出兜底时间 - 新增 Windows 原生集成测试并接入 dev/release 构建
162 lines
4.9 KiB
Go
162 lines
4.9 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestApplicationBeforeCloseEmitsPromptOnceUntilCancelled(t *testing.T) {
|
|
originalEmit := emitApplicationBeforeCloseRequest
|
|
originalQuit := quitApplicationRuntime
|
|
t.Cleanup(func() {
|
|
emitApplicationBeforeCloseRequest = originalEmit
|
|
quitApplicationRuntime = originalQuit
|
|
})
|
|
|
|
var emitted []string
|
|
emitApplicationBeforeCloseRequest = func(_ context.Context, eventName string, _ ...interface{}) {
|
|
emitted = append(emitted, eventName)
|
|
}
|
|
quitApplicationRuntime = func(context.Context) {}
|
|
|
|
app := NewAppWithSecretStore(nil)
|
|
handler := NewBeforeCloseHandler(app)
|
|
|
|
if prevent := handler(context.Background()); !prevent {
|
|
t.Fatal("expected first close request to be prevented")
|
|
}
|
|
if len(emitted) != 1 || emitted[0] != applicationBeforeCloseRequestEvent {
|
|
t.Fatalf("expected one before-close event, got %#v", emitted)
|
|
}
|
|
if prevent := handler(context.Background()); !prevent {
|
|
t.Fatal("expected repeated close request to stay prevented while prompt is open")
|
|
}
|
|
if len(emitted) != 1 {
|
|
t.Fatalf("expected no duplicate prompt event, got %#v", emitted)
|
|
}
|
|
|
|
result := app.CancelApplicationQuit()
|
|
if !result.Success {
|
|
t.Fatalf("expected cancel quit success, got %#v", result)
|
|
}
|
|
if prevent := handler(context.Background()); !prevent {
|
|
t.Fatal("expected close request after cancellation to be prevented again")
|
|
}
|
|
if len(emitted) != 2 {
|
|
t.Fatalf("expected prompt event after cancellation, got %#v", emitted)
|
|
}
|
|
}
|
|
|
|
func TestForceQuitApplicationAllowsNextCloseRequest(t *testing.T) {
|
|
originalEmit := emitApplicationBeforeCloseRequest
|
|
originalQuit := quitApplicationRuntime
|
|
t.Cleanup(func() {
|
|
emitApplicationBeforeCloseRequest = originalEmit
|
|
quitApplicationRuntime = originalQuit
|
|
})
|
|
|
|
emitApplicationBeforeCloseRequest = func(context.Context, string, ...interface{}) {}
|
|
quitCalls := 0
|
|
quitApplicationRuntime = func(context.Context) {
|
|
quitCalls++
|
|
}
|
|
|
|
app := NewAppWithSecretStore(nil)
|
|
app.ctx = context.Background()
|
|
if result := app.ForceQuitApplication(); !result.Success {
|
|
t.Fatalf("expected force quit success, got %#v", result)
|
|
}
|
|
if quitCalls != 1 {
|
|
t.Fatalf("expected runtime Quit to be called once, got %d", quitCalls)
|
|
}
|
|
if prevent := NewBeforeCloseHandler(app)(context.Background()); prevent {
|
|
t.Fatal("expected next close request to be allowed after force quit")
|
|
}
|
|
}
|
|
|
|
func TestInstallUpdateAndRestartAllowsGuardedCloseBeforeFallbackExit(t *testing.T) {
|
|
originalEmit := emitApplicationBeforeCloseRequest
|
|
originalQuit := quitApplicationRuntime
|
|
originalResolveInstallTarget := updateResolveInstallTarget
|
|
originalLaunchInstallScript := updateLaunchInstallScript
|
|
originalSleep := updateQuitSleep
|
|
originalExit := updateExitProcess
|
|
t.Cleanup(func() {
|
|
emitApplicationBeforeCloseRequest = originalEmit
|
|
quitApplicationRuntime = originalQuit
|
|
updateResolveInstallTarget = originalResolveInstallTarget
|
|
updateLaunchInstallScript = originalLaunchInstallScript
|
|
updateQuitSleep = originalSleep
|
|
updateExitProcess = originalExit
|
|
})
|
|
|
|
app := NewAppWithSecretStore(nil)
|
|
app.ctx = context.Background()
|
|
stagedDir := t.TempDir()
|
|
app.updateState.staged = &stagedUpdate{
|
|
FilePath: stagedDir + "/GoNavi-update.exe",
|
|
StagedDir: stagedDir,
|
|
}
|
|
|
|
events := make(chan string, 3)
|
|
promptEvents := make(chan struct{}, 1)
|
|
guardResults := make(chan bool, 1)
|
|
sleepDurations := make(chan time.Duration, 2)
|
|
exitCodes := make(chan int, 1)
|
|
emitApplicationBeforeCloseRequest = func(context.Context, string, ...interface{}) {
|
|
promptEvents <- struct{}{}
|
|
}
|
|
quitApplicationRuntime = func(ctx context.Context) {
|
|
events <- "quit"
|
|
guardResults <- app.beforeClose(ctx)
|
|
}
|
|
updateResolveInstallTarget = func() string {
|
|
return stagedDir + "/GoNavi.exe"
|
|
}
|
|
updateLaunchInstallScript = func(*stagedUpdate) error {
|
|
events <- "installer"
|
|
return nil
|
|
}
|
|
updateQuitSleep = func(duration time.Duration) {
|
|
sleepDurations <- duration
|
|
}
|
|
updateExitProcess = func(code int) {
|
|
events <- "exit"
|
|
exitCodes <- code
|
|
}
|
|
|
|
result := app.InstallUpdateAndRestart()
|
|
if !result.Success {
|
|
t.Fatalf("expected update installation to start, got %#v", result)
|
|
}
|
|
|
|
for index, want := range []string{"installer", "quit", "exit"} {
|
|
select {
|
|
case got := <-events:
|
|
if got != want {
|
|
t.Fatalf("event %d = %q, want %q", index, got, want)
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatalf("timed out waiting for %q event", want)
|
|
}
|
|
}
|
|
|
|
if prevented := <-guardResults; prevented {
|
|
t.Fatal("expected updater-controlled close to bypass the normal close prompt")
|
|
}
|
|
select {
|
|
case <-promptEvents:
|
|
t.Fatal("expected no before-close prompt during update restart")
|
|
default:
|
|
}
|
|
if exitCode := <-exitCodes; exitCode != 0 {
|
|
t.Fatalf("expected fallback exit code 0, got %d", exitCode)
|
|
}
|
|
for index, want := range []time.Duration{300 * time.Millisecond, 35 * time.Second} {
|
|
if got := <-sleepDurations; got != want {
|
|
t.Fatalf("quit sleep %d = %s, want %s", index, got, want)
|
|
}
|
|
}
|
|
}
|