feat: initial commit
This commit is contained in:
242
pkg/wails3kit/updates/controller_test.go
Normal file
242
pkg/wails3kit/updates/controller_test.go
Normal file
@@ -0,0 +1,242 @@
|
||||
package updates
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNormalizeVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
version, err := NormalizeVersion("1.2.3")
|
||||
if err != nil {
|
||||
t.Fatalf("NormalizeVersion returned error: %v", err)
|
||||
}
|
||||
if version != "v1.2.3" {
|
||||
t.Fatalf("NormalizeVersion returned %q", version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestControllerCheckAndDownload(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
platform := &fakePlatform{
|
||||
root: InstallRoot{Path: t.TempDir()},
|
||||
stage: StagedArtifact{
|
||||
Path: t.TempDir(),
|
||||
Root: InstallRoot{Path: t.TempDir()},
|
||||
Bundle: BundleManifest{
|
||||
SchemaVersion: 1,
|
||||
EntryPoint: "App",
|
||||
Files: []BundleFile{{Path: "App", Mode: "0755"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
release := &Release{
|
||||
ID: "1.1.0",
|
||||
Version: "1.1.0",
|
||||
Channel: ChannelStable,
|
||||
Artifact: Artifact{
|
||||
Kind: ArtifactKindBundleArchive,
|
||||
Format: ArtifactFormatZip,
|
||||
URL: "https://example.invalid/app.zip",
|
||||
SHA256: "deadbeef",
|
||||
},
|
||||
}
|
||||
|
||||
controller, err := NewController(Config{
|
||||
App: AppDescriptor{
|
||||
ProductID: "com.example.app",
|
||||
CurrentVersion: "1.0.0",
|
||||
Channel: ChannelStable,
|
||||
OS: "linux",
|
||||
Arch: "amd64",
|
||||
ExecutablePath: filepath.Join(t.TempDir(), "App"),
|
||||
},
|
||||
Provider: fakeProvider{release: release},
|
||||
Downloader: fakeDownloader{file: DownloadedFile{Path: "bundle.zip", SHA256: "deadbeef"}},
|
||||
Store: &memoryStore{},
|
||||
Platform: platform,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("NewController returned error: %v", err)
|
||||
}
|
||||
|
||||
snapshot, err := controller.Check(context.Background(), CheckRequest{})
|
||||
if err != nil {
|
||||
t.Fatalf("Check returned error: %v", err)
|
||||
}
|
||||
if snapshot.State != StateUpdateAvailable {
|
||||
t.Fatalf("Check state = %s", snapshot.State)
|
||||
}
|
||||
|
||||
snapshot, err = controller.Download(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Download returned error: %v", err)
|
||||
}
|
||||
if snapshot.State != StateReadyToApply {
|
||||
t.Fatalf("Download state = %s", snapshot.State)
|
||||
}
|
||||
if snapshot.Staged == nil {
|
||||
t.Fatal("Download did not stage artifact")
|
||||
}
|
||||
}
|
||||
|
||||
func TestControllerBusy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
block := make(chan struct{})
|
||||
controller, err := NewController(Config{
|
||||
App: AppDescriptor{
|
||||
ProductID: "com.example.app",
|
||||
CurrentVersion: "1.0.0",
|
||||
Channel: ChannelStable,
|
||||
OS: "linux",
|
||||
Arch: "amd64",
|
||||
ExecutablePath: filepath.Join(t.TempDir(), "App"),
|
||||
},
|
||||
Provider: fakeProvider{
|
||||
resolveFunc: func(context.Context, ResolveRequest) (*Release, error) {
|
||||
<-block
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
Downloader: fakeDownloader{},
|
||||
Store: &memoryStore{},
|
||||
Platform: &fakePlatform{root: InstallRoot{Path: t.TempDir()}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("NewController returned error: %v", err)
|
||||
}
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := controller.Check(context.Background(), CheckRequest{})
|
||||
done <- err
|
||||
}()
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
if _, err := controller.Check(context.Background(), CheckRequest{}); !errors.Is(err, ErrBusy) {
|
||||
t.Fatalf("expected ErrBusy, got %v", err)
|
||||
}
|
||||
close(block)
|
||||
if err := <-done; err != nil {
|
||||
t.Fatalf("first Check returned error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestControllerCleansStagedUpdateOnMatchingVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
store := &memoryStore{}
|
||||
err := store.Save(context.Background(), Snapshot{
|
||||
State: StateRestarting,
|
||||
Staged: &StagedArtifact{
|
||||
Release: Release{Version: "1.2.0"},
|
||||
},
|
||||
Candidate: &Release{Version: "1.2.0"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Save returned error: %v", err)
|
||||
}
|
||||
|
||||
controller, err := NewController(Config{
|
||||
App: AppDescriptor{
|
||||
ProductID: "com.example.app",
|
||||
CurrentVersion: "1.2.0",
|
||||
Channel: ChannelStable,
|
||||
OS: "linux",
|
||||
Arch: "amd64",
|
||||
ExecutablePath: filepath.Join(t.TempDir(), "App"),
|
||||
},
|
||||
Provider: fakeProvider{},
|
||||
Downloader: fakeDownloader{},
|
||||
Store: store,
|
||||
Platform: &fakePlatform{root: InstallRoot{Path: t.TempDir()}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("NewController returned error: %v", err)
|
||||
}
|
||||
|
||||
snapshot := controller.Snapshot()
|
||||
if snapshot.Staged != nil {
|
||||
t.Fatal("expected staged update to be cleared")
|
||||
}
|
||||
if snapshot.State != StateUpToDate {
|
||||
t.Fatalf("snapshot state = %s", snapshot.State)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeProvider struct {
|
||||
release *Release
|
||||
resolveFunc func(context.Context, ResolveRequest) (*Release, error)
|
||||
}
|
||||
|
||||
func (provider fakeProvider) Resolve(ctx context.Context, req ResolveRequest) (*Release, error) {
|
||||
if provider.resolveFunc != nil {
|
||||
return provider.resolveFunc(ctx, req)
|
||||
}
|
||||
return provider.release, nil
|
||||
}
|
||||
|
||||
func (provider fakeProvider) OpenArtifact(context.Context, Release) (io.ReadCloser, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type fakeDownloader struct {
|
||||
file DownloadedFile
|
||||
err error
|
||||
}
|
||||
|
||||
func (downloader fakeDownloader) Download(context.Context, Artifact) (DownloadedFile, error) {
|
||||
return downloader.file, downloader.err
|
||||
}
|
||||
|
||||
type fakePlatform struct {
|
||||
root InstallRoot
|
||||
stage StagedArtifact
|
||||
err error
|
||||
}
|
||||
|
||||
func (platform *fakePlatform) DetectInstallRoot(AppDescriptor) (InstallRoot, error) {
|
||||
return platform.root, platform.err
|
||||
}
|
||||
|
||||
func (platform *fakePlatform) Stage(context.Context, InstallRoot, DownloadedFile, Release) (StagedArtifact, error) {
|
||||
return platform.stage, platform.err
|
||||
}
|
||||
|
||||
func (platform *fakePlatform) SpawnApplyAndRestart(context.Context, ApplyRequest) error {
|
||||
return platform.err
|
||||
}
|
||||
|
||||
type memoryStore struct {
|
||||
mu sync.Mutex
|
||||
snapshot Snapshot
|
||||
}
|
||||
|
||||
func (store *memoryStore) Load(context.Context) (Snapshot, error) {
|
||||
store.mu.Lock()
|
||||
defer store.mu.Unlock()
|
||||
return store.snapshot, nil
|
||||
}
|
||||
|
||||
func (store *memoryStore) Save(_ context.Context, snapshot Snapshot) error {
|
||||
store.mu.Lock()
|
||||
defer store.mu.Unlock()
|
||||
store.snapshot = snapshot
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *memoryStore) ClearStaged(context.Context) error {
|
||||
store.mu.Lock()
|
||||
defer store.mu.Unlock()
|
||||
store.snapshot.Staged = nil
|
||||
store.snapshot.Candidate = nil
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user