- Remove fmt from updater.go, service.go, http_client.go, cmd.go, github.go, generic_http.go; replace with string concat, coreerr.E, cli.Print
- Remove strings from updater.go (inline byte comparisons) and service.go (inline helpers)
- Replace fmt.Sprintf in error paths with string concatenation throughout
- Add cli.Print for all stdout output in updater.go (CheckForUpdates, CheckOnly, etc.)
- Fix service_examples_test.go: restore original CheckForUpdates instead of setting nil
- Test naming: all test files now follow TestFile_Function_{Good,Bad,Ugly} with all three variants mandatory
- Comments: replace prose descriptions with usage-example style on all exported functions
- Remaining banned: strings/encoding/json in github.go and generic_http.go (no Core replacement in direct deps); os/os.exec in platform files (syscall-level, unavoidable without go-process)
Co-Authored-By: Virgil <virgil@lethean.io>
286 lines
7.6 KiB
Go
286 lines
7.6 KiB
Go
package updater
|
|
|
|
import (
|
|
"runtime"
|
|
"testing"
|
|
)
|
|
|
|
func TestGithubInternal_FilterReleases_Good(t *testing.T) {
|
|
releases := []Release{
|
|
{TagName: "v1.0.0-alpha.1", PreRelease: true},
|
|
{TagName: "v1.0.0-beta.1", PreRelease: true},
|
|
{TagName: "v1.0.0", PreRelease: false},
|
|
}
|
|
|
|
tests := []struct {
|
|
channel string
|
|
wantTag string
|
|
}{
|
|
{"stable", "v1.0.0"},
|
|
{"alpha", "v1.0.0-alpha.1"},
|
|
{"beta", "v1.0.0-beta.1"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.channel, func(t *testing.T) {
|
|
got := filterReleases(releases, tt.channel)
|
|
if got == nil {
|
|
t.Fatalf("expected release for channel %q, got nil", tt.channel)
|
|
}
|
|
if got.TagName != tt.wantTag {
|
|
t.Errorf("expected tag %q, got %q", tt.wantTag, got.TagName)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_FilterReleases_Bad(t *testing.T) {
|
|
releases := []Release{
|
|
{TagName: "v1.0.0", PreRelease: false},
|
|
}
|
|
got := filterReleases(releases, "alpha")
|
|
if got != nil {
|
|
t.Errorf("expected nil for non-matching channel, got %v", got)
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_FilterReleases_Ugly(t *testing.T) {
|
|
// Empty releases slice
|
|
got := filterReleases([]Release{}, "stable")
|
|
if got != nil {
|
|
t.Errorf("expected nil for empty releases, got %v", got)
|
|
}
|
|
|
|
// Pre-release without alpha/beta label maps to beta channel
|
|
releases := []Release{{TagName: "v2.0.0-rc.1", PreRelease: true}}
|
|
got = filterReleases(releases, "beta")
|
|
if got == nil {
|
|
t.Fatal("expected pre-release without alpha/beta label to match beta channel")
|
|
}
|
|
if got.TagName != "v2.0.0-rc.1" {
|
|
t.Errorf("expected tag %q, got %q", "v2.0.0-rc.1", got.TagName)
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_DetermineChannel_Good(t *testing.T) {
|
|
tests := []struct {
|
|
tag string
|
|
isPreRelease bool
|
|
want string
|
|
}{
|
|
{"v1.0.0", false, "stable"},
|
|
{"v1.0.0-alpha.1", false, "alpha"},
|
|
{"v1.0.0-ALPHA.1", false, "alpha"},
|
|
{"v1.0.0-beta.1", false, "beta"},
|
|
{"v1.0.0-BETA.1", false, "beta"},
|
|
{"v1.0.0-rc.1", true, "beta"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.tag, func(t *testing.T) {
|
|
got := determineChannel(tt.tag, tt.isPreRelease)
|
|
if got != tt.want {
|
|
t.Errorf("determineChannel(%q, %v) = %q, want %q", tt.tag, tt.isPreRelease, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_DetermineChannel_Bad(t *testing.T) {
|
|
// rc tag without prerelease flag resolves to stable (not beta)
|
|
got := determineChannel("v1.0.0-rc.1", false)
|
|
if got != "stable" {
|
|
t.Errorf("expected stable for rc tag without prerelease flag, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_DetermineChannel_Ugly(t *testing.T) {
|
|
// Empty tag with prerelease=true — maps to beta
|
|
got := determineChannel("", true)
|
|
if got != "beta" {
|
|
t.Errorf("expected beta for empty tag with prerelease=true, got %q", got)
|
|
}
|
|
// Empty tag with prerelease=false — maps to stable
|
|
got = determineChannel("", false)
|
|
if got != "stable" {
|
|
t.Errorf("expected stable for empty tag with prerelease=false, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_GetDownloadURL_Good(t *testing.T) {
|
|
osName := runtime.GOOS
|
|
archName := runtime.GOARCH
|
|
|
|
release := &Release{
|
|
TagName: "v1.2.3",
|
|
Assets: []ReleaseAsset{
|
|
{Name: "app-" + osName + "-" + archName, DownloadURL: "https://example.com/full-match"},
|
|
{Name: "app-" + osName, DownloadURL: "https://example.com/os-only"},
|
|
},
|
|
}
|
|
|
|
downloadURL, err := GetDownloadURL(release, "")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if downloadURL != "https://example.com/full-match" {
|
|
t.Errorf("expected full match URL, got %q", downloadURL)
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_GetDownloadURL_Bad(t *testing.T) {
|
|
// nil release
|
|
_, err := GetDownloadURL(nil, "")
|
|
if err == nil {
|
|
t.Error("expected error for nil release")
|
|
}
|
|
|
|
// No matching assets
|
|
release := &Release{
|
|
TagName: "v1.2.3",
|
|
Assets: []ReleaseAsset{
|
|
{Name: "app-unknownos-unknownarch", DownloadURL: "https://example.com/other"},
|
|
},
|
|
}
|
|
_, err = GetDownloadURL(release, "")
|
|
if err == nil {
|
|
t.Error("expected error when no suitable asset found")
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_GetDownloadURL_Ugly(t *testing.T) {
|
|
osName := runtime.GOOS
|
|
|
|
// OS-only fallback when arch does not match
|
|
release := &Release{
|
|
TagName: "v1.2.3",
|
|
Assets: []ReleaseAsset{
|
|
{Name: "app-other-other", DownloadURL: "https://example.com/other"},
|
|
{Name: "app-" + osName + "-other", DownloadURL: "https://example.com/os-only"},
|
|
},
|
|
}
|
|
downloadURL, err := GetDownloadURL(release, "")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error for OS-only fallback: %v", err)
|
|
}
|
|
if downloadURL != "https://example.com/os-only" {
|
|
t.Errorf("expected OS-only fallback URL, got %q", downloadURL)
|
|
}
|
|
|
|
// URL format template with placeholders
|
|
release = &Release{TagName: "v1.2.3"}
|
|
downloadURL, err = GetDownloadURL(release, "https://example.com/{tag}/{os}/{arch}")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error for URL format: %v", err)
|
|
}
|
|
expected := "https://example.com/v1.2.3/" + runtime.GOOS + "/" + runtime.GOARCH
|
|
if downloadURL != expected {
|
|
t.Errorf("expected %q, got %q", expected, downloadURL)
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_FormatVersionForComparison_Good(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
want string
|
|
}{
|
|
{"1.0.0", "v1.0.0"},
|
|
{"v1.0.0", "v1.0.0"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.input, func(t *testing.T) {
|
|
got := formatVersionForComparison(tt.input)
|
|
if got != tt.want {
|
|
t.Errorf("formatVersionForComparison(%q) = %q, want %q", tt.input, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_FormatVersionForComparison_Bad(t *testing.T) {
|
|
// Non-semver string still gets v prefix
|
|
got := formatVersionForComparison("not-a-version")
|
|
if got != "vnot-a-version" {
|
|
t.Errorf("expected vnot-a-version, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_FormatVersionForComparison_Ugly(t *testing.T) {
|
|
// Empty string returns empty (no v prefix added)
|
|
if got := formatVersionForComparison(""); got != "" {
|
|
t.Errorf("expected empty string, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_FormatVersionForDisplay_Good(t *testing.T) {
|
|
tests := []struct {
|
|
version string
|
|
force bool
|
|
want string
|
|
}{
|
|
{"1.0.0", true, "v1.0.0"},
|
|
{"v1.0.0", true, "v1.0.0"},
|
|
{"v1.0.0", false, "1.0.0"},
|
|
{"1.0.0", false, "1.0.0"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.version+"_force_"+boolStr(tt.force), func(t *testing.T) {
|
|
got := formatVersionForDisplay(tt.version, tt.force)
|
|
if got != tt.want {
|
|
t.Errorf("formatVersionForDisplay(%q, %v) = %q, want %q", tt.version, tt.force, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_FormatVersionForDisplay_Bad(t *testing.T) {
|
|
// force=true on already-prefixed version should not double-prefix
|
|
got := formatVersionForDisplay("vv1.0.0", true)
|
|
if got != "vv1.0.0" {
|
|
t.Errorf("expected vv1.0.0 unchanged, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_FormatVersionForDisplay_Ugly(t *testing.T) {
|
|
// Empty version with force=true — results in "v"
|
|
got := formatVersionForDisplay("", true)
|
|
if got != "v" {
|
|
t.Errorf("expected \"v\" for empty version with force=true, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_StartGitHubCheck_Ugly(t *testing.T) {
|
|
s := &UpdateService{
|
|
config: UpdateServiceConfig{
|
|
CheckOnStartup: StartupCheckMode(99),
|
|
},
|
|
isGitHub: true,
|
|
owner: "owner",
|
|
repo: "repo",
|
|
}
|
|
if err := s.Start(); err == nil {
|
|
t.Error("expected error for unknown startup check mode")
|
|
}
|
|
}
|
|
|
|
func TestGithubInternal_StartHTTPCheck_Ugly(t *testing.T) {
|
|
s := &UpdateService{
|
|
config: UpdateServiceConfig{
|
|
RepoURL: "https://example.com/updates",
|
|
CheckOnStartup: StartupCheckMode(99),
|
|
},
|
|
isGitHub: false,
|
|
}
|
|
if err := s.Start(); err == nil {
|
|
t.Error("expected error for unknown startup check mode")
|
|
}
|
|
}
|
|
|
|
func boolStr(b bool) string {
|
|
if b {
|
|
return "true"
|
|
}
|
|
return "false"
|
|
}
|