morz-infoboard/server/backend/internal/httpapi/playerstatus_test.go
Jesko Anschütz 896eade0fb Halte letzten Player-Status im Backend
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:19:17 +01:00

211 lines
5.8 KiB
Go

package httpapi
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
)
func TestHandlePlayerStatusAccepted(t *testing.T) {
store := newInMemoryPlayerStatusStore()
body := []byte(`{
"screen_id": "info01-dev",
"ts": "2026-03-22T16:00:00Z",
"status": "running",
"server_url": "http://127.0.0.1:8080",
"mqtt_broker": "tcp://127.0.0.1:1883",
"heartbeat_every_seconds": 30,
"started_at": "2026-03-22T15:59:30Z",
"last_heartbeat_at": "2026-03-22T16:00:00Z"
}`)
req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body))
w := httptest.NewRecorder()
handlePlayerStatus(store)(w, req)
if got, want := w.Code, http.StatusOK; got != want {
t.Fatalf("status = %d, want %d", got, want)
}
var response struct {
Status string `json:"status"`
}
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
t.Fatalf("Unmarshal() error = %v", err)
}
if got, want := response.Status, "accepted"; got != want {
t.Fatalf("response status = %q, want %q", got, want)
}
stored, ok := store.Get("info01-dev")
if !ok {
t.Fatal("store.Get() ok = false, want true")
}
if got, want := stored.ScreenID, "info01-dev"; got != want {
t.Fatalf("stored.ScreenID = %q, want %q", got, want)
}
}
func TestHandlePlayerStatusRejectsInvalidJSON(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewBufferString("{"))
w := httptest.NewRecorder()
handlePlayerStatus(newInMemoryPlayerStatusStore())(w, req)
if got, want := w.Code, http.StatusBadRequest; got != want {
t.Fatalf("status = %d, want %d", got, want)
}
}
func TestHandlePlayerStatusRejectsMissingScreenID(t *testing.T) {
body := []byte(`{
"screen_id": " ",
"ts": "2026-03-22T16:00:00Z",
"status": "running"
}`)
req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body))
w := httptest.NewRecorder()
handlePlayerStatus(newInMemoryPlayerStatusStore())(w, req)
if got, want := w.Code, http.StatusBadRequest; got != want {
t.Fatalf("status = %d, want %d", got, want)
}
}
func TestHandlePlayerStatusRejectsMissingTimestamp(t *testing.T) {
body := []byte(`{
"screen_id": "info01-dev",
"status": "running"
}`)
req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body))
w := httptest.NewRecorder()
handlePlayerStatus(newInMemoryPlayerStatusStore())(w, req)
if got, want := w.Code, http.StatusBadRequest; got != want {
t.Fatalf("status = %d, want %d", got, want)
}
}
func TestHandlePlayerStatusRejectsMissingStatus(t *testing.T) {
body := []byte(`{
"screen_id": "info01-dev",
"ts": "2026-03-22T16:00:00Z"
}`)
req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body))
w := httptest.NewRecorder()
handlePlayerStatus(newInMemoryPlayerStatusStore())(w, req)
if got, want := w.Code, http.StatusBadRequest; got != want {
t.Fatalf("status = %d, want %d", got, want)
}
}
func TestHandlePlayerStatusRejectsMalformedTimestamps(t *testing.T) {
body := []byte(`{
"screen_id": "info01-dev",
"ts": "not-a-time",
"status": "running"
}`)
req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body))
w := httptest.NewRecorder()
handlePlayerStatus(newInMemoryPlayerStatusStore())(w, req)
if got, want := w.Code, http.StatusBadRequest; got != want {
t.Fatalf("status = %d, want %d", got, want)
}
}
func TestHandlePlayerStatusRejectsMalformedStartedAt(t *testing.T) {
body := []byte(`{
"screen_id": "info01-dev",
"ts": "2026-03-22T16:00:00Z",
"status": "running",
"started_at": "not-a-time"
}`)
req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body))
w := httptest.NewRecorder()
handlePlayerStatus(newInMemoryPlayerStatusStore())(w, req)
if got, want := w.Code, http.StatusBadRequest; got != want {
t.Fatalf("status = %d, want %d", got, want)
}
}
func TestHandlePlayerStatusRejectsMalformedLastHeartbeatAt(t *testing.T) {
body := []byte(`{
"screen_id": "info01-dev",
"ts": "2026-03-22T16:00:00Z",
"status": "running",
"last_heartbeat_at": "not-a-time"
}`)
req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body))
w := httptest.NewRecorder()
handlePlayerStatus(newInMemoryPlayerStatusStore())(w, req)
if got, want := w.Code, http.StatusBadRequest; got != want {
t.Fatalf("status = %d, want %d", got, want)
}
}
func TestHandleGetLatestPlayerStatus(t *testing.T) {
store := newInMemoryPlayerStatusStore()
store.Save(playerStatusRecord{
ScreenID: "info01-dev",
Timestamp: "2026-03-22T16:00:00Z",
Status: "running",
ServerURL: "http://127.0.0.1:8080",
MQTTBroker: "tcp://127.0.0.1:1883",
HeartbeatEverySeconds: 30,
StartedAt: "2026-03-22T15:59:30Z",
LastHeartbeatAt: "2026-03-22T16:00:00Z",
})
req := httptest.NewRequest(http.MethodGet, "/api/v1/screens/info01-dev/status", nil)
req.SetPathValue("screenId", "info01-dev")
w := httptest.NewRecorder()
handleGetLatestPlayerStatus(store)(w, req)
if got, want := w.Code, http.StatusOK; got != want {
t.Fatalf("status = %d, want %d", got, want)
}
var response playerStatusRecord
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
t.Fatalf("Unmarshal() error = %v", err)
}
if got, want := response.ScreenID, "info01-dev"; got != want {
t.Fatalf("response.ScreenID = %q, want %q", got, want)
}
}
func TestHandleGetLatestPlayerStatusNotFound(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/screens/missing/status", nil)
req.SetPathValue("screenId", "missing")
w := httptest.NewRecorder()
handleGetLatestPlayerStatus(newInMemoryPlayerStatusStore())(w, req)
if got, want := w.Code, http.StatusNotFound; got != want {
t.Fatalf("status = %d, want %d", got, want)
}
}