Files
olcrtc/internal/supervisor/supervisor_test.go
2026-05-16 04:06:55 +03:00

178 lines
5.2 KiB
Go

package supervisor
import (
"context"
"errors"
"testing"
"time"
"github.com/openlibrecommunity/olcrtc/internal/app/session"
)
var errRunnerBoom = errors.New("boom")
const (
testProfileFirst = "first"
testProfileSecond = "second"
testProfileOne = "one"
)
func TestRunRequiresProfiles(t *testing.T) {
err := Run(context.Background(), Config{}, func(context.Context, session.Config) error { return nil })
if !errors.Is(err, ErrNoProfiles) {
t.Fatalf("Run() error = %v, want %v", err, ErrNoProfiles)
}
}
func TestRunAdvancesProfilesAndStopsAtMaxCycles(t *testing.T) {
profiles := []Profile{
{Name: testProfileFirst, Config: session.Config{Auth: "wbstream"}},
{Name: testProfileSecond, Config: session.Config{Auth: "jitsi"}},
}
var started []string
var ended []string
err := Run(context.Background(), Config{
Profiles: profiles,
RetryDelay: -1,
MaxCycles: 1,
OnProfileStart: func(profile Profile, cycle int) {
started = append(started, profile.Name)
if cycle != 1 {
t.Fatalf("cycle = %d, want 1", cycle)
}
},
OnProfileEnd: func(profile Profile, _ int, err error) {
ended = append(ended, profile.Name)
if !errors.Is(err, errRunnerBoom) {
t.Fatalf("profile %s err = %v, want %v", profile.Name, err, errRunnerBoom)
}
},
}, func(_ context.Context, cfg session.Config) error {
if cfg.Auth == "" {
t.Fatal("runner received empty auth")
}
return errRunnerBoom
})
if !errors.Is(err, ErrMaxCyclesExceeded) {
t.Fatalf("Run() error = %v, want %v", err, ErrMaxCyclesExceeded)
}
if got, want := started, []string{testProfileFirst, testProfileSecond}; !equalStrings(got, want) {
t.Fatalf("started = %v, want %v", got, want)
}
if got, want := ended, []string{testProfileFirst, testProfileSecond}; !equalStrings(got, want) {
t.Fatalf("ended = %v, want %v", got, want)
}
}
//nolint:cyclop // status history test verifies one complete failover cycle
func TestRunEmitsStatusHistory(t *testing.T) {
profiles := []Profile{
{Name: testProfileFirst, Config: session.Config{Auth: "wbstream"}},
{Name: testProfileSecond, Config: session.Config{Auth: "jitsi"}},
}
var snapshots []Status
err := Run(context.Background(), Config{
Profiles: profiles,
RetryDelay: -1,
MaxCycles: 1,
HistoryLimit: 3,
OnStatus: func(status Status) {
snapshots = append(snapshots, status)
},
}, func(_ context.Context, cfg session.Config) error {
if cfg.Auth == testProfileFirst {
t.Fatal("runner received profile name instead of config")
}
return errRunnerBoom
})
if !errors.Is(err, ErrMaxCyclesExceeded) {
t.Fatalf("Run() error = %v, want %v", err, ErrMaxCyclesExceeded)
}
if len(snapshots) != 4 {
t.Fatalf("status snapshots = %d, want 4", len(snapshots))
}
first := snapshots[0]
if first.ActiveProfile != testProfileFirst || first.ActiveProfileIndex != 0 || first.Cycle != 1 {
t.Fatalf("first status = %+v", first)
}
if first.Profiles[0].Starts != 1 || first.Profiles[0].LastStarted.IsZero() {
t.Fatalf("first profile start status = %+v", first.Profiles[0])
}
last := snapshots[len(snapshots)-1]
if last.ActiveProfile != "" || last.ActiveProfileIndex != -1 {
t.Fatalf("last active status = %+v", last)
}
if last.Profiles[0].Failures != 1 || last.Profiles[1].Failures != 1 {
t.Fatalf("profile failures = %+v", last.Profiles)
}
if last.LastError == "" || last.Profiles[1].LastError == "" {
t.Fatalf("last errors missing: %+v", last)
}
if len(last.History) != 3 {
t.Fatalf("history length = %d, want 3", len(last.History))
}
if last.History[0].Type != EventProfileEnd || last.History[0].Profile != testProfileFirst {
t.Fatalf("oldest bounded history event = %+v", last.History[0])
}
if last.History[2].Type != EventProfileEnd || last.History[2].Profile != testProfileSecond ||
last.History[2].Error == "" {
t.Fatalf("last history event = %+v", last.History[2])
}
}
func TestRunStatusSnapshotIsImmutable(t *testing.T) {
var first Status
var second Status
err := Run(context.Background(), Config{
Profiles: []Profile{{Name: testProfileOne}},
RetryDelay: -1,
MaxCycles: 1,
OnStatus: func(status Status) {
if first.Profiles == nil {
first = status
first.Profiles[0].Starts = 99
first.History[0].Profile = "mutated"
return
}
second = status
},
}, func(context.Context, session.Config) error {
return errRunnerBoom
})
if !errors.Is(err, ErrMaxCyclesExceeded) {
t.Fatalf("Run() error = %v, want %v", err, ErrMaxCyclesExceeded)
}
if first.Profiles[0].Starts != 99 || first.History[0].Profile != "mutated" {
t.Fatalf("test mutation did not apply to snapshot: %+v", first)
}
if second.Profiles[0].Starts != 1 || second.History[0].Profile != testProfileOne {
t.Fatalf("snapshot mutation leaked into later status: %+v", second)
}
}
func TestRunReturnsNilOnContextCancel(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
err := Run(ctx, Config{
Profiles: []Profile{{Name: testProfileOne}},
RetryDelay: time.Hour,
}, func(context.Context, session.Config) error {
cancel()
return nil
})
if err != nil {
t.Fatalf("Run() error = %v, want nil", err)
}
}
func equalStrings(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}