Files
netbird/client/internal/updater/manager_mode_test.go
T

178 lines
5.9 KiB
Go

package updater
import (
"context"
"path"
"testing"
"time"
v "github.com/hashicorp/go-version"
"github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/client/internal/statemanager"
)
func Test_UndecidedMode_SuppressesNotification(t *testing.T) {
tmpFile := path.Join(t.TempDir(), "update-test-undecided.json")
recorder := peer.NewRecorder("")
sub := recorder.SubscribeToEvents()
defer recorder.UnsubscribeFromEvents(sub)
mockUpdate := &versionUpdateMock{latestVersion: v.Must(v.NewSemver("1.0.1"))}
m := NewManager(recorder, statemanager.New(tmpFile))
m.update = mockUpdate
m.currentVersion = "1.0.0"
m.Start(context.Background())
defer m.Stop()
mockUpdate.onUpdate()
if ver, _ := waitForUpdateEvent(sub, 300*time.Millisecond); ver != "" {
t.Fatalf("undecided mode must not publish, got %q", ver)
}
m.NotifyUI()
if ver, _ := waitForUpdateEvent(sub, 300*time.Millisecond); ver != "" {
t.Fatalf("NotifyUI in undecided mode must not publish, got %q", ver)
}
m.SetDownloadOnly()
ver, enforced := waitForUpdateEvent(sub, 500*time.Millisecond)
if ver != "1.0.1" {
t.Fatalf("expected download-only event for 1.0.1, got %q", ver)
}
if enforced {
t.Error("download-only event must not carry enforced metadata")
}
}
func Test_ResetMode_ReturnsToUndecided(t *testing.T) {
tmpFile := path.Join(t.TempDir(), "update-test-reset.json")
recorder := peer.NewRecorder("")
sub := recorder.SubscribeToEvents()
defer recorder.UnsubscribeFromEvents(sub)
mockUpdate := &versionUpdateMock{latestVersion: v.Must(v.NewSemver("1.0.1"))}
m := NewManager(recorder, statemanager.New(tmpFile))
m.update = mockUpdate
m.currentVersion = "1.0.0"
m.autoUpdateSupported = func() bool { return true }
m.Start(context.Background())
defer m.Stop()
m.SetVersion("1.0.1", false)
ver, enforced := waitForUpdateEvent(sub, 500*time.Millisecond)
if ver != "1.0.1" || !enforced {
t.Fatalf("expected enforced event for 1.0.1, got %q enforced=%v", ver, enforced)
}
m.ResetMode()
mockUpdate.onUpdate()
if ver, _ := waitForUpdateEvent(sub, 300*time.Millisecond); ver != "" {
t.Fatalf("reset mode must not publish on fetch, got %q", ver)
}
m.NotifyUI()
if ver, _ := waitForUpdateEvent(sub, 300*time.Millisecond); ver != "" {
t.Fatalf("NotifyUI after reset must not publish, got %q", ver)
}
if err := m.Install(context.Background()); err == nil {
t.Fatal("Install after reset must fail without a pending version")
}
m.SetVersion("1.0.1", false)
ver, enforced = waitForUpdateEvent(sub, 500*time.Millisecond)
if ver != "1.0.1" || !enforced {
t.Fatalf("expected enforced event again after reset, got %q enforced=%v", ver, enforced)
}
}
func Test_SetDownloadOnly_ClearsPendingVersion(t *testing.T) {
tmpFile := path.Join(t.TempDir(), "update-test-pending.json")
recorder := peer.NewRecorder("")
sub := recorder.SubscribeToEvents()
defer recorder.UnsubscribeFromEvents(sub)
m := NewManager(recorder, statemanager.New(tmpFile))
m.update = &versionUpdateMock{latestVersion: v.Must(v.NewSemver("1.0.1"))}
m.currentVersion = "1.0.0"
m.autoUpdateSupported = func() bool { return true }
m.Start(context.Background())
defer m.Stop()
m.SetVersion("1.0.1", false)
if ver, enforced := waitForUpdateEvent(sub, 500*time.Millisecond); ver != "1.0.1" || !enforced {
t.Fatalf("expected enforced event for 1.0.1, got %q enforced=%v", ver, enforced)
}
m.SetDownloadOnly()
if ver, enforced := waitForUpdateEvent(sub, 500*time.Millisecond); ver != "1.0.1" || enforced {
t.Fatalf("expected download-only event for 1.0.1, got %q enforced=%v", ver, enforced)
}
if err := m.Install(context.Background()); err == nil {
t.Fatal("Install in download-only mode must not install the staged managed version")
}
}
func Test_ResetMode_SilencesStaleForceDirective(t *testing.T) {
tmpFile := path.Join(t.TempDir(), "update-test-force-reset.json")
recorder := peer.NewRecorder("")
sub := recorder.SubscribeToEvents()
defer recorder.UnsubscribeFromEvents(sub)
mockUpdate := &versionUpdateMock{}
m := NewManager(recorder, statemanager.New(tmpFile))
m.update = mockUpdate
m.currentVersion = "1.0.0"
m.autoUpdateSupported = func() bool { return true }
m.Start(context.Background())
defer m.Stop()
// Management enforces "latest" before the fetcher has reported any version,
// so nothing can be installed while the engine is still up.
m.SetVersion(latestVersion, true)
if event := waitForAnyEvent(sub, 300*time.Millisecond); event != nil {
t.Fatalf("no event expected before the latest version is known, got %v", event)
}
// The engine stop resets the mode. A release published afterwards must not
// trigger the stale forced install or any notification.
m.ResetMode()
mockUpdate.setLatestVersion(v.Must(v.NewSemver("1.0.1")))
mockUpdate.onUpdate()
if event := waitForAnyEvent(sub, 300*time.Millisecond); event != nil {
t.Fatalf("stale force directive must stay silent after reset, got %v", event)
}
m.SetVersion("1.0.1", false)
ver, enforced := waitForUpdateEvent(sub, 500*time.Millisecond)
if ver != "1.0.1" || !enforced {
t.Fatalf("expected enforced event after a fresh directive, got %q enforced=%v", ver, enforced)
}
}
func Test_SetVersion_MalformedFallsBackToDownloadOnly(t *testing.T) {
tmpFile := path.Join(t.TempDir(), "update-test-malformed.json")
recorder := peer.NewRecorder("")
sub := recorder.SubscribeToEvents()
defer recorder.UnsubscribeFromEvents(sub)
m := NewManager(recorder, statemanager.New(tmpFile))
m.update = &versionUpdateMock{latestVersion: v.Must(v.NewSemver("1.0.1"))}
m.currentVersion = "1.0.0"
m.autoUpdateSupported = func() bool { return true }
m.Start(context.Background())
defer m.Stop()
m.SetVersion("not-a-version", false)
ver, enforced := waitForUpdateEvent(sub, 500*time.Millisecond)
if ver != "1.0.1" {
t.Fatalf("expected download-only event for 1.0.1 after malformed version, got %q", ver)
}
if enforced {
t.Error("malformed version fallback must not carry enforced metadata")
}
}