From 9ee24fe4aee25935e9ebdc3be41901e438334ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesko=20Ansch=C3=BCtz?= Date: Sun, 22 Mar 2026 18:22:23 +0100 Subject: [PATCH] Trenne Lifecycle und Server-Connectivity im Agenten Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- player/agent/internal/app/app.go | 62 +++++++++++++++++---------- player/agent/internal/app/app_test.go | 40 +++++++++++++++++ 2 files changed, 79 insertions(+), 23 deletions(-) diff --git a/player/agent/internal/app/app.go b/player/agent/internal/app/app.go index 316f364..570fb95 100644 --- a/player/agent/internal/app/app.go +++ b/player/agent/internal/app/app.go @@ -14,20 +14,27 @@ import ( type Status string +type Connectivity string + const ( StatusStarting Status = "starting" StatusRunning Status = "running" StatusStopped Status = "stopped" + + ConnectivityUnknown Connectivity = "unknown" + ConnectivityOnline Connectivity = "online" + ConnectivityDegraded Connectivity = "degraded" ) type HealthSnapshot struct { - Status Status - ScreenID string - ServerBaseURL string - MQTTBroker string - HeartbeatEvery int - StartedAt time.Time - LastHeartbeatAt time.Time + Status Status + ServerConnectivity Connectivity + ScreenID string + ServerBaseURL string + MQTTBroker string + HeartbeatEvery int + StartedAt time.Time + LastHeartbeatAt time.Time } type App struct { @@ -36,10 +43,11 @@ type App struct { now func() time.Time reporter statusSender - mu sync.RWMutex - status Status - startedAt time.Time - lastHeartbeatAt time.Time + mu sync.RWMutex + status Status + serverConnectivity Connectivity + startedAt time.Time + lastHeartbeatAt time.Time } type statusSender interface { @@ -67,11 +75,12 @@ func newApp(cfg config.Config, logger *log.Logger, now func() time.Time, reporte } return &App{ - Config: cfg, - logger: logger, - now: now, - reporter: reporter, - status: StatusStarting, + Config: cfg, + logger: logger, + now: now, + reporter: reporter, + status: StatusStarting, + serverConnectivity: ConnectivityUnknown, } } @@ -80,13 +89,14 @@ func (a *App) Snapshot() HealthSnapshot { defer a.mu.RUnlock() return HealthSnapshot{ - Status: a.status, - ScreenID: a.Config.ScreenID, - ServerBaseURL: a.Config.ServerBaseURL, - MQTTBroker: a.Config.MQTTBroker, - HeartbeatEvery: a.Config.HeartbeatEvery, - StartedAt: a.startedAt, - LastHeartbeatAt: a.lastHeartbeatAt, + Status: a.status, + ServerConnectivity: a.serverConnectivity, + ScreenID: a.Config.ScreenID, + ServerBaseURL: a.Config.ServerBaseURL, + MQTTBroker: a.Config.MQTTBroker, + HeartbeatEvery: a.Config.HeartbeatEvery, + StartedAt: a.startedAt, + LastHeartbeatAt: a.lastHeartbeatAt, } } @@ -170,9 +180,15 @@ func (a *App) reportStatus(ctx context.Context) { LastHeartbeatAt: snapshot.LastHeartbeatAt, }) if err != nil { + a.mu.Lock() + a.serverConnectivity = ConnectivityDegraded + a.mu.Unlock() a.logger.Printf("event=status_report_failed screen_id=%s error=%v", a.Config.ScreenID, err) return } + a.mu.Lock() + a.serverConnectivity = ConnectivityOnline + a.mu.Unlock() a.logger.Printf("event=status_report_sent screen_id=%s", a.Config.ScreenID) } diff --git a/player/agent/internal/app/app_test.go b/player/agent/internal/app/app_test.go index c375815..c9f45af 100644 --- a/player/agent/internal/app/app_test.go +++ b/player/agent/internal/app/app_test.go @@ -119,6 +119,10 @@ func TestAppSnapshotIncludesConfiguredTargets(t *testing.T) { if got, want := snapshot.HeartbeatEvery, 15; got != want { t.Fatalf("HeartbeatEvery = %d, want %d", got, want) } + + if got, want := snapshot.ServerConnectivity, ConnectivityUnknown; got != want { + t.Fatalf("ServerConnectivity = %q, want %q", got, want) + } } func TestAppRunWithCanceledContextDoesNotLogConfiguredOrHeartbeat(t *testing.T) { @@ -195,4 +199,40 @@ func TestAppRunReportsStatusWithoutStoppingOnReporterError(t *testing.T) { if !strings.Contains(logs, "event=status_report_failed") { t.Fatalf("logs missing status_report_failed event: %s", logs) } + + if got, want := application.Snapshot().ServerConnectivity, ConnectivityDegraded; got != want { + t.Fatalf("ServerConnectivity = %q, want %q", got, want) + } +} + +func TestAppRunMarksServerConnectivityOnlineAfterSuccessfulReport(t *testing.T) { + application := newApp(config.Config{ + ScreenID: "screen-online", + ServerBaseURL: "http://127.0.0.1:8080", + MQTTBroker: "tcp://127.0.0.1:1883", + HeartbeatEvery: 1, + StatusReportEvery: 1, + }, log.New(&bytes.Buffer{}, "", 0), time.Now, &recordingReporter{}) + + ctx, cancel := context.WithCancel(context.Background()) + errCh := make(chan error, 1) + go func() { + errCh <- application.Run(ctx) + }() + + deadline := time.Now().Add(2 * time.Second) + for time.Now().Before(deadline) { + if application.Snapshot().ServerConnectivity == ConnectivityOnline { + break + } + time.Sleep(10 * time.Millisecond) + } + + if got, want := application.Snapshot().ServerConnectivity, ConnectivityOnline; got != want { + cancel() + t.Fatalf("ServerConnectivity = %q, want %q", got, want) + } + + cancel() + <-errCh }