mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-17 19:02:07 +08:00
- 客户端优先下载 releases/latest/download/latest.json,再回退 REST API - 成功结果写入磁盘缓存,限流/断网时可 stale 回退 - 静默检查增加节流;限流文案改为用户无需配置 Token - CI 与 build-release 发版时生成 latest.json 清单 - 补充静态清单/缓存/节流单测与生成脚本测试
189 lines
5.5 KiB
Go
189 lines
5.5 KiB
Go
package app
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestReleaseFromUpdateManifestMapsAssets(t *testing.T) {
|
|
release := releaseFromUpdateManifest(&updateReleaseManifest{
|
|
TagName: "v1.2.3",
|
|
Version: "1.2.3",
|
|
Name: "GoNavi 1.2.3",
|
|
HTMLURL: "https://github.com/Syngnat/GoNavi/releases/tag/v1.2.3",
|
|
PublishedAt: "2026-07-09T00:00:00Z",
|
|
Assets: []updateManifestAsset{
|
|
{
|
|
Name: "GoNavi-1.2.3-Windows-Amd64.exe",
|
|
URL: "https://example.com/app.exe",
|
|
SHA256: "Aa" + strings.Repeat("b", 62),
|
|
Size: 99,
|
|
},
|
|
},
|
|
})
|
|
if release == nil {
|
|
t.Fatal("expected release")
|
|
}
|
|
if release.TagName != "v1.2.3" || len(release.Assets) != 1 {
|
|
t.Fatalf("unexpected release: %#v", release)
|
|
}
|
|
if release.Assets[0].BrowserDownloadURL != "https://example.com/app.exe" {
|
|
t.Fatalf("url = %q", release.Assets[0].BrowserDownloadURL)
|
|
}
|
|
if !strings.HasPrefix(release.Assets[0].Digest, "sha256:") {
|
|
t.Fatalf("digest = %q", release.Assets[0].Digest)
|
|
}
|
|
}
|
|
|
|
func TestDiskUpdateManifestRoundTrip(t *testing.T) {
|
|
root := t.TempDir()
|
|
t.Setenv("GONAVI_DATA_ROOT", root)
|
|
|
|
payload := &updateReleaseManifest{
|
|
SchemaVersion: 1,
|
|
Channel: "latest",
|
|
TagName: "v9.9.9",
|
|
Version: "9.9.9",
|
|
Name: "v9.9.9",
|
|
HTMLURL: "https://github.com/Syngnat/GoNavi/releases/tag/v9.9.9",
|
|
Assets: []updateManifestAsset{
|
|
{Name: "app.bin", URL: "https://example.com/app.bin", SHA256: strings.Repeat("c", 64), Size: 1},
|
|
},
|
|
FetchedAt: time.Now().UTC(),
|
|
}
|
|
storeDiskUpdateManifest(updateChannelLatest, payload)
|
|
loaded, stale := loadDiskUpdateManifest(updateChannelLatest)
|
|
if loaded == nil || stale {
|
|
t.Fatalf("expected fresh disk cache, got %#v stale=%v", loaded, stale)
|
|
}
|
|
if loaded.Version != "9.9.9" {
|
|
t.Fatalf("version = %q", loaded.Version)
|
|
}
|
|
path := updateManifestCachePath(updateChannelLatest)
|
|
if _, err := os.Stat(path); err != nil {
|
|
t.Fatalf("cache file missing: %v", err)
|
|
}
|
|
if !strings.HasPrefix(path, root) {
|
|
t.Fatalf("cache path not under data root: %s", path)
|
|
}
|
|
}
|
|
|
|
func TestFetchReleaseForChannelPreferringStaticUsesStaticFirst(t *testing.T) {
|
|
root := t.TempDir()
|
|
t.Setenv("GONAVI_DATA_ROOT", root)
|
|
updateReleaseCache = sync.Map{}
|
|
|
|
staticCalled := false
|
|
apiCalled := false
|
|
restoreStatic := swapUpdateFetchStaticManifest(func(channel updateChannel) (*githubRelease, error) {
|
|
staticCalled = true
|
|
return &githubRelease{TagName: "v2.0.0", Name: "v2.0.0"}, nil
|
|
})
|
|
defer restoreStatic()
|
|
restoreAPI := swapUpdateFetchLatestRelease(func() (*githubRelease, error) {
|
|
apiCalled = true
|
|
return nil, errors.New("api should not be called")
|
|
})
|
|
defer restoreAPI()
|
|
|
|
release, err := fetchReleaseForChannelPreferringStatic(updateChannelLatest, true)
|
|
if err != nil {
|
|
t.Fatalf("err=%v", err)
|
|
}
|
|
if !staticCalled || apiCalled {
|
|
t.Fatalf("staticCalled=%v apiCalled=%v", staticCalled, apiCalled)
|
|
}
|
|
if release.TagName != "v2.0.0" {
|
|
t.Fatalf("tag=%q", release.TagName)
|
|
}
|
|
}
|
|
|
|
func TestFetchReleaseForChannelPreferringStaticFallsBackAPIThenDisk(t *testing.T) {
|
|
root := t.TempDir()
|
|
t.Setenv("GONAVI_DATA_ROOT", root)
|
|
updateReleaseCache = sync.Map{}
|
|
|
|
storeDiskUpdateManifest(updateChannelLatest, &updateReleaseManifest{
|
|
SchemaVersion: 1,
|
|
TagName: "v1.0.0",
|
|
Version: "1.0.0",
|
|
Assets: []updateManifestAsset{{Name: "a", URL: "https://example.com/a"}},
|
|
FetchedAt: time.Now().UTC(),
|
|
})
|
|
|
|
restoreStatic := swapUpdateFetchStaticManifest(func(channel updateChannel) (*githubRelease, error) {
|
|
return nil, errors.New("static 404")
|
|
})
|
|
defer restoreStatic()
|
|
restoreAPI := swapUpdateFetchLatestRelease(func() (*githubRelease, error) {
|
|
return nil, errors.New("api rate limit")
|
|
})
|
|
defer restoreAPI()
|
|
|
|
release, err := fetchReleaseForChannelPreferringStatic(updateChannelLatest, true)
|
|
if err != nil {
|
|
t.Fatalf("expected disk fallback, err=%v", err)
|
|
}
|
|
if release.TagName != "v1.0.0" {
|
|
t.Fatalf("tag=%q", release.TagName)
|
|
}
|
|
}
|
|
|
|
func TestSilentCheckThrottleReusesDisk(t *testing.T) {
|
|
root := t.TempDir()
|
|
t.Setenv("GONAVI_DATA_ROOT", root)
|
|
updateReleaseCache = sync.Map{}
|
|
storeDiskUpdateManifest(updateChannelLatest, &updateReleaseManifest{
|
|
SchemaVersion: 1,
|
|
TagName: "v3.0.0",
|
|
Version: "3.0.0",
|
|
Assets: []updateManifestAsset{{Name: "a", URL: "u"}},
|
|
FetchedAt: time.Now().UTC(),
|
|
})
|
|
markUpdateNetworkCheck(updateChannelLatest)
|
|
|
|
staticCalls := 0
|
|
restoreStatic := swapUpdateFetchStaticManifest(func(channel updateChannel) (*githubRelease, error) {
|
|
staticCalls++
|
|
return nil, errors.New("should not hit network")
|
|
})
|
|
defer restoreStatic()
|
|
restoreAPI := swapUpdateFetchLatestRelease(func() (*githubRelease, error) {
|
|
t.Fatal("api should not be called during throttle")
|
|
return nil, nil
|
|
})
|
|
defer restoreAPI()
|
|
|
|
release, err := fetchReleaseForChannelPreferringStatic(updateChannelLatest, false)
|
|
if err != nil {
|
|
t.Fatalf("err=%v", err)
|
|
}
|
|
if release.TagName != "v3.0.0" {
|
|
t.Fatalf("tag=%q", release.TagName)
|
|
}
|
|
if staticCalls != 0 {
|
|
t.Fatalf("staticCalls=%d", staticCalls)
|
|
}
|
|
}
|
|
|
|
func TestUpdateManifestFromGitHubRelease(t *testing.T) {
|
|
release := &githubRelease{
|
|
TagName: "v1.0.1",
|
|
Name: "v1.0.1",
|
|
HTMLURL: "https://github.com/Syngnat/GoNavi/releases/tag/v1.0.1",
|
|
Assets: []githubAsset{
|
|
{Name: "a.exe", BrowserDownloadURL: "https://x/a.exe", Digest: "sha256:" + strings.Repeat("d", 64), Size: 10},
|
|
},
|
|
}
|
|
m := updateManifestFromGitHubRelease(updateChannelLatest, release, nil)
|
|
if m == nil || m.Version == "" || len(m.Assets) != 1 {
|
|
t.Fatalf("manifest=%#v", m)
|
|
}
|
|
_ = json.Marshal
|
|
}
|