package sandbar import ( "net/http" "net/http/httptest" "os" "path/filepath" "runtime" "strings" "testing" ) func TestVersionNewer(t *testing.T) { for _, c := range []struct { a, b string want bool }{ {"v0.1.4", "v0.1.3", true}, {"v0.2.0", "v0.1.9", true}, {"v1.0.0", "v0.9.9", true}, {"v0.1.3", "v0.1.3", false}, {"v0.1.2", "v0.1.3", false}, {"v0.1.10", "v0.1.9", true}, {"v0.2.0-rc1", "v0.1.0", false}, // pre-releases are never offered {"v0.2.0", "dev", false}, // a development build never updates {"dev", "v0.1.0", false}, } { if got := versionNewer(c.a, c.b); got != c.want { t.Errorf("versionNewer(%q, %q) = %v, want %v", c.a, c.b, got, c.want) } } } // The update page asks the release server and reports whether it has something newer. func TestUpdateStatus(t *testing.T) { release := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/v1/repos/Veillax/sandbar/releases/latest" { http.NotFound(w, r) return } w.Write([]byte(`{"tag_name":"v0.2.0","assets":[]}`)) })) defer release.Close() s, _, _ := newTestServer(t) s.opts.Version, s.opts.UpdateServer = "v0.1.0", release.URL ts := httptest.NewServer(s.Handler()) defer ts.Close() c := newAPIClient(t, ts) if code := c.do("GET", "/api/v1/system/update", nil, nil); code != 401 { t.Fatalf("signed out: %d", code) } c.setup() var st map[string]any if code := c.do("GET", "/api/v1/system/update", nil, &st); code != 200 { t.Fatalf("status: %d", code) } if st["current"] != "v0.1.0" || st["latest"] != "v0.2.0" || st["newer"] != true { t.Fatalf("status: %v", st) } if st["error"] != "" { t.Fatalf("unexpected error: %v", st["error"]) } // A development build can look but not install. s.opts.Version = "dev" s.upd.checked = s.upd.checked.AddDate(-1, 0, 0) if code := c.do("POST", "/api/v1/system/update", map[string]string{}, &st); code != 400 { t.Fatalf("a development build must not install: %d %v", code, st) } } // A newer binary on the data volume takes over from the image, and a stale one is removed. func TestOverlay(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("self-update is not supported on Windows") } dir := t.TempDir() bin := filepath.Join(dir, "bin") os.MkdirAll(bin, 0o755) overlay := filepath.Join(bin, "sandbar") script := func(version string) { os.WriteFile(overlay, []byte("#!/bin/sh\necho 'sandbar "+version+"'\n"), 0o755) } var logged []string logf := func(f string, a ...any) { logged = append(logged, f) } // An older overlay is deleted once the image is a release that has caught up. script("v0.1.0") UseNewerOverlay(dir, "v0.1.5", logf) if _, err := os.Stat(overlay); err == nil { t.Fatal("a stale overlay should be removed") } // A development image never deletes or switches to anything. script("v0.9.0") UseNewerOverlay(dir, "dev", logf) if _, err := os.Stat(overlay); err != nil { t.Fatal("a development build must leave the overlay alone") } // Something that does not report a version is ignored, not run. os.WriteFile(overlay, []byte("#!/bin/sh\necho hello\n"), 0o755) UseNewerOverlay(dir, "v0.1.5", logf) if len(logged) == 0 || !strings.Contains(logged[len(logged)-1], "did not report a version") { t.Fatalf("log: %v", logged) } }