mirror of
https://github.com/netbirdio/netbird.git
synced 2026-03-31 06:24:18 -04:00
Auto-update logic moved out of the UI into a dedicated updatemanager.Manager service that runs in the connection layer. The UI no longer polls or checks for updates independently. The update manager supports three modes driven by the management server's auto-update policy: No policy set by mgm: checks GitHub for the latest version and notifies the user (previous behavior, now centralized) mgm enforces update: the "About" menu triggers installation directly instead of just downloading the file — user still initiates the action mgm forces update: installation proceeds automatically without user interaction updateManager lifecycle is now owned by daemon, giving the daemon server direct control via a new TriggerUpdate RPC Introduces EngineServices struct to group external service dependencies passed to NewEngine, reducing its argument count from 11 to 4
200 lines
5.6 KiB
Go
200 lines
5.6 KiB
Go
package downloader
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
retryDelay = 100 * time.Millisecond
|
|
)
|
|
|
|
func TestDownloadToFile_Success(t *testing.T) {
|
|
// Create a test server that responds successfully
|
|
content := "test file content"
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(content))
|
|
}))
|
|
defer server.Close()
|
|
|
|
// Create a temporary file for download
|
|
tempDir := t.TempDir()
|
|
dstFile := filepath.Join(tempDir, "downloaded.txt")
|
|
|
|
// Download the file
|
|
err := DownloadToFile(context.Background(), retryDelay, server.URL, dstFile)
|
|
if err != nil {
|
|
t.Fatalf("expected no error, got: %v", err)
|
|
}
|
|
|
|
// Verify the file content
|
|
data, err := os.ReadFile(dstFile)
|
|
if err != nil {
|
|
t.Fatalf("failed to read downloaded file: %v", err)
|
|
}
|
|
|
|
if string(data) != content {
|
|
t.Errorf("expected content %q, got %q", content, string(data))
|
|
}
|
|
}
|
|
|
|
func TestDownloadToFile_SuccessAfterRetry(t *testing.T) {
|
|
content := "test file content after retry"
|
|
var attemptCount atomic.Int32
|
|
|
|
// Create a test server that fails on first attempt, succeeds on second
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
attempt := attemptCount.Add(1)
|
|
if attempt == 1 {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
_, _ = w.Write([]byte("error"))
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(content))
|
|
}))
|
|
defer server.Close()
|
|
|
|
// Create a temporary file for download
|
|
tempDir := t.TempDir()
|
|
dstFile := filepath.Join(tempDir, "downloaded.txt")
|
|
|
|
// Download the file (should succeed after retry)
|
|
if err := DownloadToFile(context.Background(), 10*time.Millisecond, server.URL, dstFile); err != nil {
|
|
t.Fatalf("expected no error after retry, got: %v", err)
|
|
}
|
|
|
|
// Verify the file content
|
|
data, err := os.ReadFile(dstFile)
|
|
if err != nil {
|
|
t.Fatalf("failed to read downloaded file: %v", err)
|
|
}
|
|
|
|
if string(data) != content {
|
|
t.Errorf("expected content %q, got %q", content, string(data))
|
|
}
|
|
|
|
// Verify it took 2 attempts
|
|
if attemptCount.Load() != 2 {
|
|
t.Errorf("expected 2 attempts, got %d", attemptCount.Load())
|
|
}
|
|
}
|
|
|
|
func TestDownloadToFile_FailsAfterRetry(t *testing.T) {
|
|
var attemptCount atomic.Int32
|
|
|
|
// Create a test server that always fails
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
attemptCount.Add(1)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
_, _ = w.Write([]byte("error"))
|
|
}))
|
|
defer server.Close()
|
|
|
|
// Create a temporary file for download
|
|
tempDir := t.TempDir()
|
|
dstFile := filepath.Join(tempDir, "downloaded.txt")
|
|
|
|
// Download the file (should fail after retry)
|
|
if err := DownloadToFile(context.Background(), 10*time.Millisecond, server.URL, dstFile); err == nil {
|
|
t.Fatal("expected error after retry, got nil")
|
|
}
|
|
|
|
// Verify it tried 2 times
|
|
if attemptCount.Load() != 2 {
|
|
t.Errorf("expected 2 attempts, got %d", attemptCount.Load())
|
|
}
|
|
}
|
|
|
|
func TestDownloadToFile_ContextCancellationDuringRetry(t *testing.T) {
|
|
var attemptCount atomic.Int32
|
|
|
|
// Create a test server that always fails
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
attemptCount.Add(1)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
}))
|
|
defer server.Close()
|
|
|
|
// Create a temporary file for download
|
|
tempDir := t.TempDir()
|
|
dstFile := filepath.Join(tempDir, "downloaded.txt")
|
|
|
|
// Create a context that will be cancelled during retry delay
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
// Cancel after a short delay (during the retry sleep)
|
|
go func() {
|
|
time.Sleep(100 * time.Millisecond)
|
|
cancel()
|
|
}()
|
|
|
|
// Download the file (should fail due to context cancellation during retry)
|
|
err := DownloadToFile(ctx, 1*time.Second, server.URL, dstFile)
|
|
if err == nil {
|
|
t.Fatal("expected error due to context cancellation, got nil")
|
|
}
|
|
|
|
// Should have only made 1 attempt (cancelled during retry delay)
|
|
if attemptCount.Load() != 1 {
|
|
t.Errorf("expected 1 attempt, got %d", attemptCount.Load())
|
|
}
|
|
}
|
|
|
|
func TestDownloadToFile_InvalidURL(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
dstFile := filepath.Join(tempDir, "downloaded.txt")
|
|
|
|
err := DownloadToFile(context.Background(), retryDelay, "://invalid-url", dstFile)
|
|
if err == nil {
|
|
t.Fatal("expected error for invalid URL, got nil")
|
|
}
|
|
}
|
|
|
|
func TestDownloadToFile_InvalidDestination(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte("test"))
|
|
}))
|
|
defer server.Close()
|
|
|
|
// Use an invalid destination path
|
|
err := DownloadToFile(context.Background(), retryDelay, server.URL, "/invalid/path/that/does/not/exist/file.txt")
|
|
if err == nil {
|
|
t.Fatal("expected error for invalid destination, got nil")
|
|
}
|
|
}
|
|
|
|
func TestDownloadToFile_NoRetry(t *testing.T) {
|
|
var attemptCount atomic.Int32
|
|
|
|
// Create a test server that always fails
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
attemptCount.Add(1)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
_, _ = w.Write([]byte("error"))
|
|
}))
|
|
defer server.Close()
|
|
|
|
// Create a temporary file for download
|
|
tempDir := t.TempDir()
|
|
dstFile := filepath.Join(tempDir, "downloaded.txt")
|
|
|
|
// Download the file with retryDelay = 0 (should not retry)
|
|
if err := DownloadToFile(context.Background(), 0, server.URL, dstFile); err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
|
|
// Verify it only made 1 attempt (no retry)
|
|
if attemptCount.Load() != 1 {
|
|
t.Errorf("expected 1 attempt, got %d", attemptCount.Load())
|
|
}
|
|
}
|