package appupdate import ( "context" "crypto/sha256" "encoding/hex" "encoding/json" "log/slog" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "pipu/api/internal/api/gen" ) func TestPickClientFiles(t *testing.T) { files := pickClientFiles([]asset{ {Name: "pipu-music-1.3.0.apk", Size: 10}, {Name: "pipu-music-1.3.0.apk.sha256", Size: 65, URL: "https://gitea/sum"}, {Name: "pipu-music_1.3.0_x64-setup.exe", Size: 20}, {Name: "pipu-music_1.3.0_amd64.deb", Size: 11}, {Name: "pipu-music_1.3.0_amd64.AppImage", Size: 30}, {Name: "pipu-music_1.3.0_aarch64.dmg", Size: 40}, }) assert.Equal(t, "pipu-music-1.3.0.apk", files["android"].Name) assert.Equal(t, "https://gitea/sum", files["android"].ChecksumURL) assert.Equal(t, "pipu-music_1.3.0_x64-setup.exe", files["windows"].Name) assert.Equal(t, "pipu-music_1.3.0_amd64.deb", files["linux"].Name) assert.Equal(t, "pipu-music_1.3.0_aarch64.dmg", files["macos"].Name) } func TestVersionCode(t *testing.T) { cases := map[string]int{"v0.6.1": 60_199, "1.2.0-rc.3": 1_020_003, "v10.0.0": 10_000_099} for tag, want := range cases { _, got, err := VersionCode(tag) require.NoError(t, err, tag) assert.Equal(t, want, got, tag) } for _, bad := range []string{"latest", "v1.100.0", "1.2"} { _, _, err := VersionCode(bad) assert.Error(t, err, bad) } } func TestLatestAndDownload(t *testing.T) { apk := []byte("fake apk bytes") sum := sha256.Sum256(apk) var srv *httptest.Server srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, "token secret", r.Header.Get("Authorization")) assets := []map[string]any{ {"name": "pipu-music-0.6.1.apk", "size": len(apk), "browser_download_url": srv.URL + "/apk"}, {"name": "pipu-music-0.6.1.apk.sha256", "size": 65, "browser_download_url": srv.URL + "/sum"}, } switch r.URL.Path { case "/api/v1/repos/o/r/releases": _ = json.NewEncoder(w).Encode([]map[string]any{ {"tag_name": "v0.6.1", "body": "## Новое", "draft": false, "published_at": "2026-01-02T00:00:00Z", "assets": assets}, {"tag_name": "v0.6.0", "body": "", "draft": false, "published_at": "2026-01-01T00:00:00Z", "assets": []map[string]any{ {"name": "pipu-music-0.6.0.apk", "size": 8, "browser_download_url": srv.URL + "/apk-old"}, {"name": "pipu-music-0.6.0.apk.sha256", "size": 65, "browser_download_url": srv.URL + "/sum-old"}, {"name": "pipu-music-0.6.0-windows-setup.exe", "size": 9, "browser_download_url": srv.URL + "/exe-old"}, }}, }) case "/api/v1/repos/o/r/releases/latest": _ = json.NewEncoder(w).Encode(map[string]any{ "tag_name": "v0.6.1", "body": "## Новое", "assets": assets, }) case "/apk": _, _ = w.Write(apk) case "/sum": _, _ = w.Write([]byte(hex.EncodeToString(sum[:]) + "\n")) case "/sum-old": _, _ = w.Write([]byte(strings.Repeat("ab", 32) + "\n")) default: http.NotFound(w, r) } })) defer srv.Close() dir := t.TempDir() h := NewHandler(Config{GiteaURL: srv.URL, Repo: "o/r", Token: "secret", Dir: dir, MinSupported: 60_000}, slog.Default()) l, err := h.latest(context.Background()) require.NoError(t, err) assert.Equal(t, "0.6.1", l.Version) assert.Equal(t, 60_199, l.VersionCode) android := l.Files["android"] assert.Equal(t, hex.EncodeToString(sum[:]), android.SHA256) path, err := h.ensure(context.Background(), android) require.NoError(t, err) got, _ := os.ReadFile(path) assert.Equal(t, apk, got) // Подменённый файл не пройдёт сверку. require.NoError(t, os.Remove(path)) android.SHA256 = "00" + android.SHA256[2:] _, err = h.ensure(context.Background(), android) assert.ErrorContains(t, err, "checksum mismatch") _, statErr := os.Stat(filepath.Join(dir, "pipu-music-0.6.1.apk")) assert.True(t, os.IsNotExist(statErr)) out, err := h.GetAppClients(context.Background(), gen.GetAppClientsRequestObject{}) require.NoError(t, err) body := out.(gen.GetAppClients200JSONResponse) var androidClient, windowsClient *gen.AppClientFile for i := range body.Clients { switch body.Clients[i].Client { case gen.AppClientAndroid: androidClient = &body.Clients[i] case gen.AppClientWindows: windowsClient = &body.Clients[i] } } require.NotNil(t, androidClient) require.NotNil(t, androidClient.Older) assert.Equal(t, "0.6.0", (*androidClient.Older)[0].Version) require.NotNil(t, windowsClient) assert.Contains(t, windowsClient.Filename, "0.6.0") assert.True(t, windowsClient.Older == nil || len(*windowsClient.Older) == 0) }