feat: close STREAMING.md production readiness gaps
CI / test (push) Failing after 9s
CI / docker (push) Skipped
Deploy to Cloudflare Pages / deploy (push) Failing after 10s
CD / deploy (push) Successful in 47s

- Added run ID + unique segment naming (r-{ISOTIME}/seg-{seq}.m4s)
- Added fsnotify watcher for real-time R2 segment upload (~7s)
- Added atomic manifest publication (temp key → CopyObject)
- Fixed manifest Cache-Control: public, max-age=1, s-maxage=2, must-revalidate
- Added hls.js error recovery: bounded retries (5x), exponential backoff, reconnecting UI
- Enriched /health endpoint: run_id, active_item, last_segment_seq, last_r2_pub, consecutive_failures, unhealthy detection
- Updated tests: HealthHandler (new fields + unhealthy), player error recovery test
- Updated TODO.md, BACKLOG.md, DONE.md, MEMORY.md
This commit is contained in:
zwitschi committed 2026-08-10 17:20:54 +02:00
1 parent f6ca89747e
commit 18459a9811
12 files changed
+699 -48

No files matched your search

+42 -9
View File
@@ -241,11 +241,15 @@ func TestHealthHandler(t *testing.T) {
// Save and restore globals
prevMode := playoutMode
prevCount := restartCount
prevRunID := runID
playoutMode = "live"
restartCount = 3
runID = "r-20260810T120000Z"
lastR2PubTime = time.Now()
defer func() {
playoutMode = prevMode
restartCount = prevCount
runID = prevRunID
}()
req := httptest.NewRequest("GET", "/health", nil)
@@ -257,21 +261,30 @@ func TestHealthHandler(t *testing.T) {
}
body := w.Body.String()
if !strings.Contains(body, `"healthy"`) {
t.Errorf("body missing healthy: %s", body)
}
if !strings.Contains(body, `"playout"`) {
t.Errorf("body missing service: %s", body)
}
if !strings.Contains(body, `"restarts":3`) {
t.Errorf("body missing restarts: %s", body)
for _, want := range []string{
`"status":"healthy"`,
`"service":"playout"`,
`"mode":"live"`,
`"restarts":3`,
`"run_id":"r-20260810T120000Z"`,
`"last_segment_seq"`,
`"consecutive_failures"`,
} {
if !strings.Contains(body, want) {
t.Errorf("body missing %q: %s", want, body)
}
}
}
func TestHealthHandler_Degraded(t *testing.T) {
prevMode := playoutMode
prevR2Pub := lastR2PubTime
playoutMode = "slate"
defer func() { playoutMode = prevMode }()
lastR2PubTime = time.Now() // fresh R2 pub = not unhealthy
defer func() {
playoutMode = prevMode
lastR2PubTime = prevR2Pub
}()
req := httptest.NewRequest("GET", "/health", nil)
w := httptest.NewRecorder()
@@ -286,6 +299,26 @@ func TestHealthHandler_Degraded(t *testing.T) {
}
}
func TestHealthHandler_Unhealthy(t *testing.T) {
prevMode := playoutMode
prevR2Pub := lastR2PubTime
playoutMode = "live"
lastR2PubTime = time.Now().Add(-30 * time.Second) // stale R2 = unhealthy
defer func() {
playoutMode = prevMode
lastR2PubTime = prevR2Pub
}()
req := httptest.NewRequest("GET", "/health", nil)
w := httptest.NewRecorder()
HealthHandler(w, req)
body := w.Body.String()
if !strings.Contains(body, `"unhealthy"`) {
t.Errorf("body missing unhealthy (stale R2 pub should trigger unhealthy): %s", body)
}
}
func TestShouldEscalateToSlate(t *testing.T) {
// Save globals
prevCount := restartCount