package main import ( "os" "regexp" "strings" "testing" ) func TestWaitPidArg(t *testing.T) { for _, tc := range []struct { name string args []string want int }{ {"after the flag", []string{"--post-update", "--wait-pid", "4321"}, 4321}, {"joined with =", []string{"--wait-pid=4321"}, 4321}, {"absent", []string{"--post-update"}, 0}, {"flag with nothing after it", []string{"--wait-pid"}, 0}, {"not a number", []string{"--wait-pid", "later"}, 0}, } { if got := waitPidArg(tc.args); got != tc.want { t.Errorf("%s: got %d, wanted %d", tc.name, got, tc.want) } } } // A relaunch started from INSIDE this process has to tell the new one which // process to wait for. // // The update path no longer does that from inside: its PowerShell helper waits // for the pid and starts the exe once we are gone, which is what two rewrites // failed to reproduce. RestartApp still spawns directly, and if a relaunch is // ever added the same way without --wait-pid, it is the old bug again. func TestEveryRelaunchPassesItsPid(t *testing.T) { // Both spellings: a relaunch built inline with exec.Command, and one built // through relaunchCmd. The pattern used to name only the first, and when the // update path moved to a PowerShell helper and RestartApp to relaunchCmd it // quietly matched nothing at all — a guard that passes because it looks // nowhere. spawn := regexp.MustCompile(`(exec\.Command|relaunchCmd)\(exe, "--(post-update|relaunch)"[^)]*\)`) for _, file := range []string{"update.go", "app.go"} { src, err := os.ReadFile(file) if err != nil { t.Fatalf("read %s: %v", file, err) } for _, call := range spawn.FindAllString(string(src), -1) { if !strings.Contains(call, "--wait-pid") { t.Errorf("%s: %s does not tell the new instance which process to wait for", file, call) } } } }