first commit

This commit is contained in:
2026-08-14 23:41:57 +08:00
commit 086803a8dd
471 changed files with 91938 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
package selfupdate
import (
"context"
"net/http"
"net/http/httptest"
"testing"
)
func TestIsReleaseVersion(t *testing.T) {
cases := map[string]bool{
"": false,
"dev": false,
"unknown": false,
" dev ": false,
"v0.4.0": true,
"0.4.0": true,
}
for in, want := range cases {
if got := IsReleaseVersion(in); got != want {
t.Errorf("IsReleaseVersion(%q) = %v, want %v", in, got, want)
}
}
}
func TestUpdateAvailable(t *testing.T) {
tests := []struct {
name string
current, latest string
wantAvail, wantOK bool
}{
{"update available", "v0.3.1", "v0.4.0", true, true},
{"patch update", "0.4.0", "0.4.1", true, true},
{"already latest", "v0.4.0", "v0.4.0", false, true},
{"current newer", "v0.5.0", "v0.4.0", false, true},
{"prerelease latest", "v0.4.0", "v0.4.1-next", true, true},
{"dev current not comparable", "dev", "v0.4.0", false, false},
{"unknown current not comparable", "unknown", "v0.4.0", false, false},
{"unparseable latest", "v0.4.0", "not-a-version", false, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
avail, ok := UpdateAvailable(tt.current, tt.latest)
if avail != tt.wantAvail || ok != tt.wantOK {
t.Errorf("UpdateAvailable(%q,%q) = (%v,%v), want (%v,%v)",
tt.current, tt.latest, avail, ok, tt.wantAvail, tt.wantOK)
}
})
}
}
func TestParseVersion(t *testing.T) {
tests := []struct {
in string
want [3]int
ok bool
}{
{"v1.2.3", [3]int{1, 2, 3}, true},
{"1.2.3", [3]int{1, 2, 3}, true},
{"0.4.0-next", [3]int{0, 4, 0}, true},
{"1.2", [3]int{1, 2, 0}, true},
{"", [3]int{}, false},
{"vabc", [3]int{}, false},
}
for _, tt := range tests {
got, ok := parseVersion(tt.in)
if got != tt.want || ok != tt.ok {
t.Errorf("parseVersion(%q) = (%v,%v), want (%v,%v)", tt.in, got, ok, tt.want, tt.ok)
}
}
}
func TestLatestTag(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Accept") != "application/vnd.github+json" {
t.Errorf("missing Accept header")
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"tag_name":"v0.4.0","name":"pigo 0.4.0"}`))
}))
defer srv.Close()
// LatestTag builds the URL from repo; use a transport that redirects to the
// test server regardless of host.
client := srv.Client()
client.Transport = rewriteHost{base: srv.URL, rt: client.Transport}
tag, err := LatestTag(context.Background(), client, "smallnest/pigo")
if err != nil {
t.Fatalf("LatestTag: %v", err)
}
if tag != "v0.4.0" {
t.Errorf("tag = %q, want v0.4.0", tag)
}
}
func TestLatestTagErrors(t *testing.T) {
t.Run("non-200", func(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusForbidden)
}))
defer srv.Close()
client := srv.Client()
client.Transport = rewriteHost{base: srv.URL, rt: client.Transport}
if _, err := LatestTag(context.Background(), client, "smallnest/pigo"); err == nil {
t.Fatal("expected error on 403")
}
})
t.Run("empty tag", func(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(`{"tag_name":""}`))
}))
defer srv.Close()
client := srv.Client()
client.Transport = rewriteHost{base: srv.URL, rt: client.Transport}
if _, err := LatestTag(context.Background(), client, "smallnest/pigo"); err == nil {
t.Fatal("expected error on empty tag_name")
}
})
}
// rewriteHost redirects every request to base, so tests can point the fixed
// GitHub API URL at an httptest server.
type rewriteHost struct {
base string
rt http.RoundTripper
}
func (rw rewriteHost) RoundTrip(req *http.Request) (*http.Response, error) {
u, err := req.URL.Parse(rw.base)
if err != nil {
return nil, err
}
req.URL.Scheme = u.Scheme
req.URL.Host = u.Host
rt := rw.rt
if rt == nil {
rt = http.DefaultTransport
}
return rt.RoundTrip(req)
}