mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 11:30:15 +00:00
27 test cases covering: - NewUpdaterRegistry initialization - Register and Get operations - Overwrite behavior when re-registering - Error handling for unregistered deployment types - Multiple deployment types - UpdateRequest, UpdatePlan, UpdateProgress struct fields - Mock updater interface compliance Coverage 41.1% → 41.5%.
328 lines
8.4 KiB
Go
328 lines
8.4 KiB
Go
package updates
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
// mockUpdater implements the Updater interface for testing
|
|
type mockUpdater struct {
|
|
deploymentType string
|
|
supportsApply bool
|
|
prepareErr error
|
|
executeErr error
|
|
rollbackErr error
|
|
}
|
|
|
|
func (m *mockUpdater) SupportsApply() bool {
|
|
return m.supportsApply
|
|
}
|
|
|
|
func (m *mockUpdater) PrepareUpdate(ctx context.Context, request UpdateRequest) (*UpdatePlan, error) {
|
|
if m.prepareErr != nil {
|
|
return nil, m.prepareErr
|
|
}
|
|
return &UpdatePlan{
|
|
CanAutoUpdate: m.supportsApply,
|
|
}, nil
|
|
}
|
|
|
|
func (m *mockUpdater) Execute(ctx context.Context, request UpdateRequest, progressCb ProgressCallback) error {
|
|
return m.executeErr
|
|
}
|
|
|
|
func (m *mockUpdater) Rollback(ctx context.Context, eventID string) error {
|
|
return m.rollbackErr
|
|
}
|
|
|
|
func (m *mockUpdater) GetDeploymentType() string {
|
|
return m.deploymentType
|
|
}
|
|
|
|
func TestNewUpdaterRegistry(t *testing.T) {
|
|
registry := NewUpdaterRegistry()
|
|
if registry == nil {
|
|
t.Fatal("NewUpdaterRegistry() returned nil")
|
|
}
|
|
if registry.updaters == nil {
|
|
t.Fatal("NewUpdaterRegistry() did not initialize updaters map")
|
|
}
|
|
}
|
|
|
|
func TestUpdaterRegistry_Register(t *testing.T) {
|
|
registry := NewUpdaterRegistry()
|
|
updater := &mockUpdater{deploymentType: "docker"}
|
|
|
|
registry.Register("docker", updater)
|
|
|
|
// Verify the updater was registered
|
|
got, err := registry.Get("docker")
|
|
if err != nil {
|
|
t.Fatalf("Get() after Register() failed: %v", err)
|
|
}
|
|
if got != updater {
|
|
t.Error("Get() returned different updater than registered")
|
|
}
|
|
}
|
|
|
|
func TestUpdaterRegistry_Register_Overwrites(t *testing.T) {
|
|
registry := NewUpdaterRegistry()
|
|
updater1 := &mockUpdater{deploymentType: "docker", supportsApply: false}
|
|
updater2 := &mockUpdater{deploymentType: "docker", supportsApply: true}
|
|
|
|
registry.Register("docker", updater1)
|
|
registry.Register("docker", updater2)
|
|
|
|
got, err := registry.Get("docker")
|
|
if err != nil {
|
|
t.Fatalf("Get() failed: %v", err)
|
|
}
|
|
if got != updater2 {
|
|
t.Error("Register() did not overwrite previous updater")
|
|
}
|
|
if !got.SupportsApply() {
|
|
t.Error("Expected second updater with SupportsApply=true")
|
|
}
|
|
}
|
|
|
|
func TestUpdaterRegistry_Get_NotFound(t *testing.T) {
|
|
registry := NewUpdaterRegistry()
|
|
|
|
_, err := registry.Get("nonexistent")
|
|
if err == nil {
|
|
t.Fatal("Get() should return error for unregistered deployment type")
|
|
}
|
|
|
|
expectedMsg := "no updater registered for deployment type: nonexistent"
|
|
if err.Error() != expectedMsg {
|
|
t.Errorf("Get() error = %q, want %q", err.Error(), expectedMsg)
|
|
}
|
|
}
|
|
|
|
func TestUpdaterRegistry_MultipleDeploymentTypes(t *testing.T) {
|
|
registry := NewUpdaterRegistry()
|
|
|
|
dockerUpdater := &mockUpdater{deploymentType: "docker"}
|
|
installshUpdater := &mockUpdater{deploymentType: "install.sh"}
|
|
aurUpdater := &mockUpdater{deploymentType: "aur"}
|
|
|
|
registry.Register("docker", dockerUpdater)
|
|
registry.Register("install.sh", installshUpdater)
|
|
registry.Register("aur", aurUpdater)
|
|
|
|
tests := []struct {
|
|
deploymentType string
|
|
expectedType string
|
|
}{
|
|
{"docker", "docker"},
|
|
{"install.sh", "install.sh"},
|
|
{"aur", "aur"},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.deploymentType, func(t *testing.T) {
|
|
got, err := registry.Get(tc.deploymentType)
|
|
if err != nil {
|
|
t.Fatalf("Get(%q) failed: %v", tc.deploymentType, err)
|
|
}
|
|
if got.GetDeploymentType() != tc.expectedType {
|
|
t.Errorf("GetDeploymentType() = %q, want %q", got.GetDeploymentType(), tc.expectedType)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUpdateRequest_Fields(t *testing.T) {
|
|
req := UpdateRequest{
|
|
Version: "1.2.3",
|
|
Channel: "stable",
|
|
Force: true,
|
|
DryRun: false,
|
|
}
|
|
|
|
if req.Version != "1.2.3" {
|
|
t.Errorf("Version = %q, want %q", req.Version, "1.2.3")
|
|
}
|
|
if req.Channel != "stable" {
|
|
t.Errorf("Channel = %q, want %q", req.Channel, "stable")
|
|
}
|
|
if !req.Force {
|
|
t.Error("Force should be true")
|
|
}
|
|
if req.DryRun {
|
|
t.Error("DryRun should be false")
|
|
}
|
|
}
|
|
|
|
func TestUpdatePlan_Fields(t *testing.T) {
|
|
plan := UpdatePlan{
|
|
CanAutoUpdate: true,
|
|
Instructions: []string{"step1", "step2"},
|
|
Prerequisites: []string{"prereq1"},
|
|
EstimatedTime: "5 minutes",
|
|
RequiresRoot: true,
|
|
RollbackSupport: true,
|
|
DownloadURL: "https://example.com/download",
|
|
}
|
|
|
|
if !plan.CanAutoUpdate {
|
|
t.Error("CanAutoUpdate should be true")
|
|
}
|
|
if len(plan.Instructions) != 2 {
|
|
t.Errorf("Instructions length = %d, want 2", len(plan.Instructions))
|
|
}
|
|
if len(plan.Prerequisites) != 1 {
|
|
t.Errorf("Prerequisites length = %d, want 1", len(plan.Prerequisites))
|
|
}
|
|
if plan.EstimatedTime != "5 minutes" {
|
|
t.Errorf("EstimatedTime = %q, want %q", plan.EstimatedTime, "5 minutes")
|
|
}
|
|
if !plan.RequiresRoot {
|
|
t.Error("RequiresRoot should be true")
|
|
}
|
|
if !plan.RollbackSupport {
|
|
t.Error("RollbackSupport should be true")
|
|
}
|
|
if plan.DownloadURL != "https://example.com/download" {
|
|
t.Errorf("DownloadURL = %q, want %q", plan.DownloadURL, "https://example.com/download")
|
|
}
|
|
}
|
|
|
|
func TestUpdateProgress_Fields(t *testing.T) {
|
|
progress := UpdateProgress{
|
|
Stage: "downloading",
|
|
Progress: 50,
|
|
Message: "Downloading update...",
|
|
IsComplete: false,
|
|
Error: "",
|
|
}
|
|
|
|
if progress.Stage != "downloading" {
|
|
t.Errorf("Stage = %q, want %q", progress.Stage, "downloading")
|
|
}
|
|
if progress.Progress != 50 {
|
|
t.Errorf("Progress = %d, want 50", progress.Progress)
|
|
}
|
|
if progress.Message != "Downloading update..." {
|
|
t.Errorf("Message = %q, want %q", progress.Message, "Downloading update...")
|
|
}
|
|
if progress.IsComplete {
|
|
t.Error("IsComplete should be false")
|
|
}
|
|
if progress.Error != "" {
|
|
t.Errorf("Error = %q, want empty string", progress.Error)
|
|
}
|
|
}
|
|
|
|
func TestUpdateProgress_WithError(t *testing.T) {
|
|
progress := UpdateProgress{
|
|
Stage: "failed",
|
|
Progress: 0,
|
|
Message: "Update failed",
|
|
IsComplete: true,
|
|
Error: "connection timeout",
|
|
}
|
|
|
|
if !progress.IsComplete {
|
|
t.Error("IsComplete should be true for failed state")
|
|
}
|
|
if progress.Error != "connection timeout" {
|
|
t.Errorf("Error = %q, want %q", progress.Error, "connection timeout")
|
|
}
|
|
}
|
|
|
|
func TestMockUpdater_SupportsApply(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
supports bool
|
|
}{
|
|
{"supports apply", true},
|
|
{"does not support apply", false},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
updater := &mockUpdater{supportsApply: tc.supports}
|
|
if updater.SupportsApply() != tc.supports {
|
|
t.Errorf("SupportsApply() = %v, want %v", updater.SupportsApply(), tc.supports)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMockUpdater_PrepareUpdate(t *testing.T) {
|
|
ctx := context.Background()
|
|
req := UpdateRequest{Version: "1.0.0"}
|
|
|
|
t.Run("success", func(t *testing.T) {
|
|
updater := &mockUpdater{supportsApply: true}
|
|
plan, err := updater.PrepareUpdate(ctx, req)
|
|
if err != nil {
|
|
t.Fatalf("PrepareUpdate() failed: %v", err)
|
|
}
|
|
if !plan.CanAutoUpdate {
|
|
t.Error("CanAutoUpdate should be true")
|
|
}
|
|
})
|
|
|
|
t.Run("error", func(t *testing.T) {
|
|
expectedErr := errors.New("prepare failed")
|
|
updater := &mockUpdater{prepareErr: expectedErr}
|
|
_, err := updater.PrepareUpdate(ctx, req)
|
|
if err != expectedErr {
|
|
t.Errorf("PrepareUpdate() error = %v, want %v", err, expectedErr)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMockUpdater_Execute(t *testing.T) {
|
|
ctx := context.Background()
|
|
req := UpdateRequest{Version: "1.0.0"}
|
|
progressCb := func(p UpdateProgress) {}
|
|
|
|
t.Run("success", func(t *testing.T) {
|
|
updater := &mockUpdater{}
|
|
err := updater.Execute(ctx, req, progressCb)
|
|
if err != nil {
|
|
t.Errorf("Execute() error = %v, want nil", err)
|
|
}
|
|
})
|
|
|
|
t.Run("error", func(t *testing.T) {
|
|
expectedErr := errors.New("execute failed")
|
|
updater := &mockUpdater{executeErr: expectedErr}
|
|
err := updater.Execute(ctx, req, progressCb)
|
|
if err != expectedErr {
|
|
t.Errorf("Execute() error = %v, want %v", err, expectedErr)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMockUpdater_Rollback(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
t.Run("success", func(t *testing.T) {
|
|
updater := &mockUpdater{}
|
|
err := updater.Rollback(ctx, "event-123")
|
|
if err != nil {
|
|
t.Errorf("Rollback() error = %v, want nil", err)
|
|
}
|
|
})
|
|
|
|
t.Run("error", func(t *testing.T) {
|
|
expectedErr := errors.New("rollback failed")
|
|
updater := &mockUpdater{rollbackErr: expectedErr}
|
|
err := updater.Rollback(ctx, "event-123")
|
|
if err != expectedErr {
|
|
t.Errorf("Rollback() error = %v, want %v", err, expectedErr)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMockUpdater_GetDeploymentType(t *testing.T) {
|
|
updater := &mockUpdater{deploymentType: "custom-type"}
|
|
if got := updater.GetDeploymentType(); got != "custom-type" {
|
|
t.Errorf("GetDeploymentType() = %q, want %q", got, "custom-type")
|
|
}
|
|
}
|