mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-06 05:29:07 +02:00
228 lines
7.2 KiB
Go
228 lines
7.2 KiB
Go
//go:build windows || darwin
|
|
|
|
package updater
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path"
|
|
"testing"
|
|
"time"
|
|
|
|
v "github.com/hashicorp/go-version"
|
|
|
|
"github.com/netbirdio/netbird/client/internal/peer"
|
|
"github.com/netbirdio/netbird/client/internal/statemanager"
|
|
cProto "github.com/netbirdio/netbird/client/proto"
|
|
)
|
|
|
|
func Test_LatestVersion(t *testing.T) {
|
|
testMatrix := []struct {
|
|
name string
|
|
daemonVersion string
|
|
initialLatestVersion *v.Version
|
|
latestVersion *v.Version
|
|
shouldUpdateInit bool
|
|
shouldUpdateLater bool
|
|
}{
|
|
{
|
|
name: "Should notify again when a newer version arrives even within 5 minutes",
|
|
daemonVersion: "1.0.0",
|
|
initialLatestVersion: v.Must(v.NewSemver("1.0.1")),
|
|
latestVersion: v.Must(v.NewSemver("1.0.2")),
|
|
shouldUpdateInit: true,
|
|
shouldUpdateLater: true,
|
|
},
|
|
{
|
|
name: "Shouldn't update initially, but should update as soon as latest version is fetched",
|
|
daemonVersion: "1.0.0",
|
|
initialLatestVersion: nil,
|
|
latestVersion: v.Must(v.NewSemver("1.0.1")),
|
|
shouldUpdateInit: false,
|
|
shouldUpdateLater: true,
|
|
},
|
|
}
|
|
|
|
for idx, c := range testMatrix {
|
|
mockUpdate := &versionUpdateMock{latestVersion: c.initialLatestVersion}
|
|
tmpFile := path.Join(t.TempDir(), fmt.Sprintf("update-test-%d.json", idx))
|
|
recorder := peer.NewRecorder("")
|
|
sub := recorder.SubscribeToEvents()
|
|
defer recorder.UnsubscribeFromEvents(sub)
|
|
|
|
m := NewManager(recorder, statemanager.New(tmpFile))
|
|
m.update = mockUpdate
|
|
m.currentVersion = c.daemonVersion
|
|
m.autoUpdateSupported = func() bool { return true }
|
|
m.Start(context.Background())
|
|
m.SetVersion("latest", false)
|
|
|
|
ver, _ := waitForUpdateEvent(sub, 500*time.Millisecond)
|
|
triggeredInit := ver != ""
|
|
if triggeredInit != c.shouldUpdateInit {
|
|
t.Errorf("%s: Initial update trigger mismatch, expected %v, got %v", c.name, c.shouldUpdateInit, triggeredInit)
|
|
}
|
|
if triggeredInit && c.initialLatestVersion != nil && ver != c.initialLatestVersion.String() {
|
|
t.Errorf("%s: Initial update version mismatch, expected %v, got %v", c.name, c.initialLatestVersion.String(), ver)
|
|
}
|
|
|
|
mockUpdate.setLatestVersion(c.latestVersion)
|
|
mockUpdate.onUpdate()
|
|
|
|
ver, _ = waitForUpdateEvent(sub, 500*time.Millisecond)
|
|
triggeredLater := ver != ""
|
|
if triggeredLater != c.shouldUpdateLater {
|
|
t.Errorf("%s: Later update trigger mismatch, expected %v, got %v", c.name, c.shouldUpdateLater, triggeredLater)
|
|
}
|
|
if triggeredLater && c.latestVersion != nil && ver != c.latestVersion.String() {
|
|
t.Errorf("%s: Later update version mismatch, expected %v, got %v", c.name, c.latestVersion.String(), ver)
|
|
}
|
|
|
|
m.Stop()
|
|
}
|
|
}
|
|
|
|
func Test_HandleUpdate(t *testing.T) {
|
|
testMatrix := []struct {
|
|
name string
|
|
daemonVersion string
|
|
latestVersion *v.Version
|
|
expectedVersion string
|
|
shouldUpdate bool
|
|
}{
|
|
{
|
|
name: "Install to a specific version should update regardless of if latestVersion is available yet",
|
|
daemonVersion: "0.55.0",
|
|
latestVersion: nil,
|
|
expectedVersion: "0.56.0",
|
|
shouldUpdate: true,
|
|
},
|
|
{
|
|
name: "Install to specific version should not update if version matches",
|
|
daemonVersion: "0.55.0",
|
|
latestVersion: nil,
|
|
expectedVersion: "0.55.0",
|
|
shouldUpdate: false,
|
|
},
|
|
{
|
|
name: "Install to specific version should not update if current version is newer",
|
|
daemonVersion: "0.55.0",
|
|
latestVersion: nil,
|
|
expectedVersion: "0.54.0",
|
|
shouldUpdate: false,
|
|
},
|
|
{
|
|
name: "Install to latest version should update if latest is newer",
|
|
daemonVersion: "0.55.0",
|
|
latestVersion: v.Must(v.NewSemver("0.56.0")),
|
|
expectedVersion: "latest",
|
|
shouldUpdate: true,
|
|
},
|
|
{
|
|
name: "Install to latest version should not update if latest == current",
|
|
daemonVersion: "0.56.0",
|
|
latestVersion: v.Must(v.NewSemver("0.56.0")),
|
|
expectedVersion: "latest",
|
|
shouldUpdate: false,
|
|
},
|
|
{
|
|
name: "Should not update if daemon version is invalid",
|
|
daemonVersion: "development",
|
|
latestVersion: v.Must(v.NewSemver("1.0.0")),
|
|
expectedVersion: "latest",
|
|
shouldUpdate: false,
|
|
},
|
|
{
|
|
name: "Should not update if expecting latest and latest version is unavailable",
|
|
daemonVersion: "0.55.0",
|
|
latestVersion: nil,
|
|
expectedVersion: "latest",
|
|
shouldUpdate: false,
|
|
},
|
|
{
|
|
name: "Should not update if expected version is invalid",
|
|
daemonVersion: "0.55.0",
|
|
latestVersion: nil,
|
|
expectedVersion: "development",
|
|
shouldUpdate: false,
|
|
},
|
|
}
|
|
for idx, c := range testMatrix {
|
|
tmpFile := path.Join(t.TempDir(), fmt.Sprintf("update-test-%d.json", idx))
|
|
recorder := peer.NewRecorder("")
|
|
sub := recorder.SubscribeToEvents()
|
|
defer recorder.UnsubscribeFromEvents(sub)
|
|
|
|
m := NewManager(recorder, statemanager.New(tmpFile))
|
|
m.update = &versionUpdateMock{latestVersion: c.latestVersion}
|
|
m.currentVersion = c.daemonVersion
|
|
m.autoUpdateSupported = func() bool { return true }
|
|
m.Start(context.Background())
|
|
m.SetVersion(c.expectedVersion, false)
|
|
|
|
ver, _ := waitForUpdateEvent(sub, 500*time.Millisecond)
|
|
updateTriggered := ver != ""
|
|
|
|
if updateTriggered {
|
|
if c.expectedVersion == "latest" && c.latestVersion != nil && ver != c.latestVersion.String() {
|
|
t.Errorf("%s: Version mismatch, expected %v, got %v", c.name, c.latestVersion.String(), ver)
|
|
} else if c.expectedVersion != "latest" && c.expectedVersion != "development" && ver != c.expectedVersion {
|
|
t.Errorf("%s: Version mismatch, expected %v, got %v", c.name, c.expectedVersion, ver)
|
|
}
|
|
}
|
|
|
|
if updateTriggered != c.shouldUpdate {
|
|
t.Errorf("%s: Update trigger mismatch, expected %v, got %v", c.name, c.shouldUpdate, updateTriggered)
|
|
}
|
|
m.Stop()
|
|
}
|
|
}
|
|
|
|
func Test_EnforcedMetadata(t *testing.T) {
|
|
// Mode 1 (downloadOnly): no enforced metadata
|
|
tmpFile := path.Join(t.TempDir(), "update-test-mode1.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.Start(context.Background())
|
|
m.SetDownloadOnly()
|
|
|
|
ver, enforced := waitForUpdateEvent(sub, 500*time.Millisecond)
|
|
if ver == "" {
|
|
t.Fatal("Mode 1: expected new_version_available event")
|
|
}
|
|
if enforced {
|
|
t.Error("Mode 1: expected no enforced metadata")
|
|
}
|
|
m.Stop()
|
|
|
|
// Mode 2 (enforced, forceUpdate=false): enforced metadata present, no auto-install
|
|
tmpFile2 := path.Join(t.TempDir(), "update-test-mode2.json")
|
|
recorder2 := peer.NewRecorder("")
|
|
sub2 := recorder2.SubscribeToEvents()
|
|
defer recorder2.UnsubscribeFromEvents(sub2)
|
|
|
|
m2 := NewManager(recorder2, statemanager.New(tmpFile2))
|
|
m2.update = &versionUpdateMock{latestVersion: nil}
|
|
m2.currentVersion = "1.0.0"
|
|
m2.autoUpdateSupported = func() bool { return true }
|
|
m2.Start(context.Background())
|
|
m2.SetVersion("1.0.1", false)
|
|
|
|
ver, enforced2 := waitForUpdateEvent(sub2, 500*time.Millisecond)
|
|
if ver == "" {
|
|
t.Fatal("Mode 2: expected new_version_available event")
|
|
}
|
|
if !enforced2 {
|
|
t.Error("Mode 2: expected enforced metadata")
|
|
}
|
|
m2.Stop()
|
|
}
|
|
|
|
// ensure the proto import is used
|
|
var _ = cProto.SystemEvent_INFO
|