80 lines
2.7 KiB
Go
80 lines
2.7 KiB
Go
package web
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"path/filepath"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
|
)
|
|
|
|
// R-1282: the household's „later" media restart (03:30) was an in-memory timer — a controller restart before then
|
|
// (the night's self-update, a power cut) forgot it and the media apps never saw the new drive. The time is now stored;
|
|
// at start it is re-armed, a time that passed while the controller was down fires at once, and it runs ONCE.
|
|
// RED-PROOF: make ResumeMediaLater a no-op → the passed case never restarts the apps.
|
|
func r1282Server(t *testing.T) (*Server, *settings.Settings, *int32) {
|
|
t.Helper()
|
|
lg := log.New(io.Discard, "", 0)
|
|
sett, err := settings.Load(filepath.Join(t.TempDir(), "settings.json"), lg)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var runs int32
|
|
old := mediaLaterRestart
|
|
mediaLaterRestart = func(*Server, string) { atomic.AddInt32(&runs, 1) }
|
|
t.Cleanup(func() { mediaLaterRestart = old })
|
|
return &Server{settings: sett, logger: lg}, sett, &runs
|
|
}
|
|
|
|
func TestR1282_ALaterRestartThatPassedWhileDownRunsOnceAtStart(t *testing.T) {
|
|
s, sett, runs := r1282Server(t)
|
|
if err := sett.SetMediaRestartLater(time.Now().Add(-2 * time.Hour)); err != nil { // 03:30 passed while down
|
|
t.Fatal(err)
|
|
}
|
|
s.ResumeMediaLater()
|
|
deadline := time.Now().Add(2 * time.Second)
|
|
for atomic.LoadInt32(runs) == 0 && time.Now().Before(deadline) {
|
|
time.Sleep(5 * time.Millisecond)
|
|
}
|
|
if n := atomic.LoadInt32(runs); n != 1 {
|
|
t.Fatalf("the passed restart ran %d time(s) at start, want 1", n)
|
|
}
|
|
for !sett.GetMediaRestartLater().IsZero() && time.Now().Before(deadline) {
|
|
time.Sleep(5 * time.Millisecond)
|
|
}
|
|
if !sett.GetMediaRestartLater().IsZero() {
|
|
t.Fatal("the stored time must be cleared once it ran — else the next start runs it again")
|
|
}
|
|
// A second start finds nothing: it ran once.
|
|
s.ResumeMediaLater()
|
|
time.Sleep(50 * time.Millisecond)
|
|
if n := atomic.LoadInt32(runs); n != 1 {
|
|
t.Fatalf("a second start ran it again (%d)", n)
|
|
}
|
|
}
|
|
|
|
func TestR1282_AFutureLaterRestartIsKeptOverAStart(t *testing.T) {
|
|
s, sett, runs := r1282Server(t)
|
|
at := time.Now().Add(1 * time.Hour)
|
|
if err := sett.SetMediaRestartLater(at); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s.ResumeMediaLater()
|
|
time.Sleep(50 * time.Millisecond)
|
|
if n := atomic.LoadInt32(runs); n != 0 {
|
|
t.Fatalf("a restart due in an hour ran now (%d)", n)
|
|
}
|
|
s.mediaMu.Lock()
|
|
got, armed := s.mediaLaterAt, s.mediaLaterTmr != nil
|
|
s.mediaMu.Unlock()
|
|
if !armed || !got.Equal(at.UTC().Truncate(time.Second)) && got.Sub(at).Abs() > time.Second {
|
|
t.Fatalf("the timer must be armed for %v, got %v (armed %v)", at, got, armed)
|
|
}
|
|
if s.mediaLaterTmr != nil {
|
|
s.mediaLaterTmr.Stop()
|
|
}
|
|
}
|