Files
pipu-music/services/api/internal/appupdate/appupdate_test.go
T
mrleo1nidandCursor 39aaaed969
ci / api / api (push) Successful in 48s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 58s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m17s
ci / ts / ts (packages + web) (push) Successful in 2m1s
ci / db / db (push) Successful in 1m4s
feat(web): list previous app builds on the download page
GET /app/clients now returns up to 8 Gitea releases per OS so users can download older installers while they still exist after prune.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-22 08:40:08 +03:00

128 lines
4.5 KiB
Go

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)
}