Files

79 lines
2.6 KiB
Go
Raw Permalink Normal View History

package transcodenode
import (
"context"
"errors"
"testing"
"time"
"github.com/Silo-Server/silo-server/internal/playback"
)
// The node's segment-recovery restart must also run under the per-session
// lifecycle lock so it cannot race a fresh start or reconstruct into the same
// output directory. These tests exercise the wrapper's gating and liveness
// re-check without spawning ffmpeg (the playback package covers the real spawn).
// restartSessionLocked must block while another lifecycle holder owns the lock,
// and re-check session liveness once it acquires it — a session torn down while
// the restart waited must yield ErrSessionSuperseded rather than a stale spawn.
func TestRestartSessionLocked_WaitsForLockThenRechecks(t *testing.T) {
s := &Server{sessions: map[string]*playback.TranscodeSession{}}
const sid = "sess-x"
sess := &playback.TranscodeSession{}
s.mu.Lock()
s.sessions[sid] = sess
s.mu.Unlock()
// A concurrent start/reconstruct holds the lifecycle lock.
release := s.lockSessionLifecycle(sid)
done := make(chan error, 1)
go func() {
done <- s.restartSessionLocked(context.Background(), sid, sess, 0, 0)
}()
select {
case <-done:
t.Fatal("restartSessionLocked returned while the lifecycle lock was held")
case <-time.After(150 * time.Millisecond):
}
// Simulate a teardown removing the session while the restart is blocked, so
// the post-lock re-check fails and no ffmpeg is spawned.
s.mu.Lock()
delete(s.sessions, sid)
s.mu.Unlock()
release()
select {
case err := <-done:
if !errors.Is(err, playback.ErrSessionSuperseded) {
t.Fatalf("want ErrSessionSuperseded after teardown, got %v", err)
}
case <-time.After(2 * time.Second):
t.Fatal("restartSessionLocked did not complete after lock release")
}
}
func TestRestartSessionLocked_SupersededWhenUnregistered(t *testing.T) {
s := &Server{sessions: map[string]*playback.TranscodeSession{}}
err := s.restartSessionLocked(context.Background(), "sess-x", &playback.TranscodeSession{}, 0, 0)
if !errors.Is(err, playback.ErrSessionSuperseded) {
t.Fatalf("want ErrSessionSuperseded for an unmapped session, got %v", err)
}
}
func TestRestartSessionLocked_SupersededWhenReplaced(t *testing.T) {
s := &Server{sessions: map[string]*playback.TranscodeSession{}}
const sid = "sess-x"
s.mu.Lock()
s.sessions[sid] = &playback.TranscodeSession{} // a different session owns the id
s.mu.Unlock()
err := s.restartSessionLocked(context.Background(), sid, &playback.TranscodeSession{}, 0, 0)
if !errors.Is(err, playback.ErrSessionSuperseded) {
t.Fatalf("want ErrSessionSuperseded for a replaced session, got %v", err)
}
}