package main import ( "bytes" "context" "errors" "fmt" "io" "net/http" "net/http/httptest" "strings" "testing" ) func releaseServer(t *testing.T, tag string, withInstaller bool) *httptest.Server { t.Helper() assets := "" if withInstaller { assets = fmt.Sprintf(`{"name":%q,"browser_download_url":"https://x/setup.exe"}`, windowsInstallerAsset) } srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprintf(w, `{"tag_name":%q,"html_url":"https://x/release","assets":[%s]}`, tag, assets) })) t.Cleanup(srv.Close) return srv } func TestRunUpdate(t *testing.T) { tests := []struct { name string current string latest string withInstaller bool goos string checkOnly bool assumeYes bool stdin string wantOut []string wantInstall bool }{ {name: "up to date", current: "v1.0.5", latest: "v1.0.5", goos: "windows", withInstaller: true, wantOut: []string{"up to date"}}, {name: "dev build never prompts", current: "dev", latest: "v9.0.0", goos: "windows", withInstaller: true, wantOut: []string{"up to date"}}, {name: "check only", current: "v1.0.5", latest: "v1.0.6", goos: "windows", withInstaller: true, checkOnly: true, wantOut: []string{"v1.0.6", "https://x/release"}}, {name: "non-windows shows url", current: "v1.0.5", latest: "v1.0.6", goos: "linux", withInstaller: true, wantOut: []string{"Download it from: https://x/release"}}, {name: "windows without installer asset", current: "v1.0.5", latest: "v1.0.6", goos: "windows", wantOut: []string{"Download it from"}}, {name: "windows prompt yes", current: "v1.0.5", latest: "v1.0.6", goos: "windows", withInstaller: true, stdin: "y\n", wantOut: []string{"[y/N]"}, wantInstall: true}, {name: "windows prompt no", current: "v1.0.5", latest: "v1.0.6", goos: "windows", withInstaller: true, stdin: "n\n", wantOut: []string{"Skipped"}}, {name: "windows prompt empty", current: "v1.0.5", latest: "v1.0.6", goos: "windows", withInstaller: true, wantOut: []string{"Skipped"}}, {name: "windows --yes", current: "v1.0.5", latest: "v1.0.6", goos: "windows", withInstaller: true, assumeYes: true, wantInstall: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { srv := releaseServer(t, tt.latest, tt.withInstaller) var out bytes.Buffer var installedURL string err := runUpdate(context.Background(), updateOptions{ current: tt.current, apiURL: srv.URL, goos: tt.goos, checkOnly: tt.checkOnly, assumeYes: tt.assumeYes, in: strings.NewReader(tt.stdin), out: &out, install: func(_ context.Context, url string, _ io.Writer) error { installedURL = url return nil }, }) if err != nil { t.Fatal(err) } for _, want := range tt.wantOut { if !strings.Contains(out.String(), want) { t.Errorf("output missing %q:\n%s", want, out.String()) } } if (installedURL != "") != tt.wantInstall { t.Errorf("installer called = %v, want %v", installedURL != "", tt.wantInstall) } if tt.wantInstall && installedURL != "https://x/setup.exe" { t.Errorf("installer url = %q", installedURL) } }) } } func TestRunUpdate_Errors(t *testing.T) { bad := httptest.NewServer(http.NotFoundHandler()) defer bad.Close() if err := runUpdate(context.Background(), updateOptions{current: "v1.0.0", apiURL: bad.URL, out: io.Discard}); err == nil { t.Error("expected lookup error") } srv := releaseServer(t, "v1.0.6", true) want := errors.New("boom") err := runUpdate(context.Background(), updateOptions{ current: "v1.0.5", apiURL: srv.URL, goos: "windows", assumeYes: true, out: io.Discard, install: func(context.Context, string, io.Writer) error { return want }, }) if !errors.Is(err, want) { t.Errorf("err = %v", err) } } func TestDownloadAndRunInstaller_BadStatus(t *testing.T) { srv := httptest.NewServer(http.NotFoundHandler()) defer srv.Close() if err := downloadAndRunInstaller(context.Background(), srv.URL, io.Discard); err == nil { t.Error("expected error for non-200") } if err := downloadAndRunInstaller(context.Background(), "://bad", io.Discard); err == nil { t.Error("expected error for bad url") } }