| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- //go:build unit
- package service
- import (
- "context"
- "errors"
- "testing"
- "time"
- "github.com/stretchr/testify/require"
- )
- type updateServiceCacheStub struct {
- data string
- }
- func (s *updateServiceCacheStub) GetUpdateInfo(context.Context) (string, error) {
- if s.data == "" {
- return "", errors.New("cache miss")
- }
- return s.data, nil
- }
- func (s *updateServiceCacheStub) SetUpdateInfo(_ context.Context, data string, _ time.Duration) error {
- s.data = data
- return nil
- }
- type updateServiceGitHubClientStub struct {
- release *GitHubRelease
- recentReleases []*GitHubRelease
- recentErr error
- }
- func (s *updateServiceGitHubClientStub) FetchLatestRelease(context.Context, string) (*GitHubRelease, error) {
- return s.release, nil
- }
- func (s *updateServiceGitHubClientStub) FetchRecentReleases(context.Context, string, int) ([]*GitHubRelease, error) {
- return s.recentReleases, s.recentErr
- }
- func (s *updateServiceGitHubClientStub) DownloadFile(context.Context, string, string, int64) error {
- panic("DownloadFile should not be called when no update is available")
- }
- func (s *updateServiceGitHubClientStub) FetchChecksumFile(context.Context, string) ([]byte, error) {
- panic("FetchChecksumFile should not be called when no update is available")
- }
- func TestUpdateServiceDistributionDisablesUpstreamMutation(t *testing.T) {
- svc := NewUpdateService(
- &updateServiceCacheStub{},
- &updateServiceGitHubClientStub{
- release: &GitHubRelease{
- TagName: "v0.1.132",
- Name: "v0.1.132",
- },
- },
- "0.1.132",
- "release",
- )
- err := svc.PerformUpdate(context.Background())
- require.Error(t, err)
- require.ErrorIs(t, err, ErrManagedUpdateRequired)
- require.ErrorIs(t, svc.Rollback(), ErrManagedUpdateRequired)
- require.ErrorIs(t, svc.RollbackToVersion(context.Background(), "0.1.100"), ErrManagedUpdateRequired)
- info, err := svc.CheckUpdate(context.Background(), true)
- require.NoError(t, err)
- require.False(t, info.HasUpdate)
- require.Equal(t, "source", info.BuildType)
- }
- func newRollbackTestService(current string, releases []*GitHubRelease) *UpdateService {
- svc := NewUpdateService(
- &updateServiceCacheStub{},
- &updateServiceGitHubClientStub{recentReleases: releases},
- current,
- "release",
- )
- // Exercise legacy version-selection helpers without enabling production updates.
- svc.inPlaceUpdatesEnabled = true
- return svc
- }
- func TestUpdateServiceListRollbackVersionsFiltersAndCaps(t *testing.T) {
- releases := []*GitHubRelease{
- {TagName: "v0.1.148", PublishedAt: "2026-07-09T00:00:00Z"}, // newer than current: excluded
- {TagName: "v0.1.147", PublishedAt: "2026-07-08T00:00:00Z"}, // current: excluded
- {TagName: "v0.1.146-rc1", PublishedAt: "2026-07-07T12:00:00Z", Prerelease: true}, // prerelease: excluded
- {TagName: "v0.1.146", PublishedAt: "2026-07-07T00:00:00Z"},
- {TagName: "v0.1.145", PublishedAt: "2026-07-06T00:00:00Z", Draft: true}, // draft: excluded
- {TagName: "v0.1.144", PublishedAt: "2026-07-05T00:00:00Z"},
- {TagName: "v0.1.144", PublishedAt: "2026-07-05T00:00:00Z"}, // duplicate: excluded
- {TagName: "v0.1.143", PublishedAt: "2026-07-04T00:00:00Z"},
- {TagName: "v0.1.142", PublishedAt: "2026-07-03T00:00:00Z"}, // beyond cap of 3: excluded
- }
- svc := newRollbackTestService("0.1.147", releases)
- versions, err := svc.ListRollbackVersions(context.Background())
- require.NoError(t, err)
- require.Len(t, versions, 3)
- require.Equal(t, "0.1.146", versions[0].Version)
- require.Equal(t, "0.1.144", versions[1].Version)
- require.Equal(t, "0.1.143", versions[2].Version)
- }
- func TestUpdateServiceListRollbackVersionsSortsUnorderedInput(t *testing.T) {
- releases := []*GitHubRelease{
- {TagName: "v0.1.144"},
- {TagName: "v0.1.146"},
- {TagName: "v0.1.145"},
- }
- svc := newRollbackTestService("0.1.147", releases)
- versions, err := svc.ListRollbackVersions(context.Background())
- require.NoError(t, err)
- require.Len(t, versions, 3)
- require.Equal(t, "0.1.146", versions[0].Version)
- require.Equal(t, "0.1.145", versions[1].Version)
- require.Equal(t, "0.1.144", versions[2].Version)
- }
- func TestUpdateServiceListRollbackVersionsEmptyWhenNoneOlder(t *testing.T) {
- releases := []*GitHubRelease{
- {TagName: "v0.1.147"},
- {TagName: "v0.1.148"},
- }
- svc := newRollbackTestService("0.1.147", releases)
- versions, err := svc.ListRollbackVersions(context.Background())
- require.NoError(t, err)
- require.Empty(t, versions)
- }
- func TestUpdateServiceListRollbackVersionsPropagatesFetchError(t *testing.T) {
- svc := NewUpdateService(
- &updateServiceCacheStub{},
- &updateServiceGitHubClientStub{recentErr: errors.New("github unavailable")},
- "0.1.147",
- "release",
- )
- svc.inPlaceUpdatesEnabled = true
- _, err := svc.ListRollbackVersions(context.Background())
- require.Error(t, err)
- require.Contains(t, err.Error(), "github unavailable")
- }
- func TestUpdateServiceRollbackToVersionRejectsDisallowedTargets(t *testing.T) {
- releases := []*GitHubRelease{
- {TagName: "v0.1.148"},
- {TagName: "v0.1.147"},
- {TagName: "v0.1.146"},
- {TagName: "v0.1.145"},
- {TagName: "v0.1.144"},
- {TagName: "v0.1.143"},
- {TagName: "v0.1.142"},
- }
- svc := newRollbackTestService("0.1.147", releases)
- for _, target := range []string{
- "", // empty
- "0.1.147", // current version
- "v0.1.147", // current version with prefix
- "0.1.148", // newer than current
- "0.1.142", // older than the 3 most recent
- "9.9.9", // nonexistent
- } {
- err := svc.RollbackToVersion(context.Background(), target)
- require.ErrorIs(t, err, ErrRollbackVersionNotAllowed, "target %q should be rejected", target)
- }
- }
- func TestUpdateServiceRollbackToVersionAcceptsVPrefix(t *testing.T) {
- // No platform asset in the release: the target passes the allowlist check
- // and fails later at asset lookup, proving the version itself was accepted.
- releases := []*GitHubRelease{
- {TagName: "v0.1.147"},
- {TagName: "v0.1.146"},
- }
- svc := newRollbackTestService("0.1.147", releases)
- err := svc.RollbackToVersion(context.Background(), "v0.1.146")
- require.Error(t, err)
- require.NotErrorIs(t, err, ErrRollbackVersionNotAllowed)
- require.Contains(t, err.Error(), "no compatible release found")
- }
|