diff --git a/internal/api/app.go b/internal/api/app.go index 3db7a93..a7286fa 100644 --- a/internal/api/app.go +++ b/internal/api/app.go @@ -1944,6 +1944,8 @@ func (a *App) syncStatus() (*SyncStatusDTO, error) { switch { case dto.Revoked: dto.StatusLabel = "revoked" + case dto.LastError != "": + dto.StatusLabel = "error" case dto.Connected: dto.StatusLabel = "connected" case dto.Configured: @@ -2002,6 +2004,7 @@ func (a *App) syncConfigure(serverURL, username, password string) error { cfg.Sync.DeviceID = deviceID cfg.Sync.DeviceName = hostname cfg.Sync.LastStatus = "connected" + cfg.Sync.LastError = "" _ = a.appSettings.UpdateSync(cfg.Sync) return nil diff --git a/internal/api/app_test.go b/internal/api/app_test.go index 7afb4b6..0b80803 100644 --- a/internal/api/app_test.go +++ b/internal/api/app_test.go @@ -1645,6 +1645,55 @@ func TestPluginSyncBridgeRequiresDeclaredPermissions(t *testing.T) { } } +func TestPluginSyncStatusReportsPersistedError(t *testing.T) { + app := newBridgeTestApp(t) + app.plugins = append(app.plugins, + plugin.Plugin{ + Manifest: plugin.Manifest{ + ID: "sync.local", + Name: "Sync Local", + Version: "1.0.0", + Provides: []string{"sync/local/v1"}, + Permissions: []string{"sync.participate"}, + }, + Status: plugin.StatusLoaded, + Enabled: true, + }, + ) + app.syncSvc = syncsvc.NewService(app.vaultPath(), "local-device") + app.appSettings = appsettings.NewManager(filepath.Join(t.TempDir(), "config.json")) + if err := app.appSettings.Load(); err != nil { + t.Fatalf("settings Load: %v", err) + } + if err := app.syncSvc.SetState("https://sync.example.test", ""); err != nil { + t.Fatalf("SetState: %v", err) + } + cfg := app.appSettings.Get() + cfg.Sync.Enabled = true + cfg.Sync.ServerURL = "https://sync.example.test" + cfg.Sync.DeviceID = "device-1" + cfg.Sync.LastStatus = "error" + cfg.Sync.LastError = "push: server unavailable" + if err := app.appSettings.UpdateSync(cfg.Sync); err != nil { + t.Fatalf("settings UpdateSync: %v", err) + } + if err := syncsvc.SaveDeviceToken(app.vaultPath(), "secret-token"); err != nil { + t.Fatalf("SaveDeviceToken: %v", err) + } + + status, errStr := app.PluginSyncStatus("sync.local") + if errStr != "" { + t.Fatalf("PluginSyncStatus: %s", errStr) + } + if status.StatusLabel != "error" || status.LastError != "push: server unavailable" { + t.Fatalf("status = %#v, want persisted sync error", status) + } + cfg = app.appSettings.Get() + if cfg.Sync.LastStatus != "error" || cfg.Sync.LastError != "push: server unavailable" { + t.Fatalf("persisted sync settings = %#v, want error preserved", cfg.Sync) + } +} + func TestPluginBridgeCapabilitiesCommandsAndEventsAreChecked(t *testing.T) { app := newBridgeTestApp(t)