challenge_wiring_test.go
Rama main · réplica de lectura del repositorio original.
package main
import (
"context"
"testing"
"time"
"github.com/pingular/pingularity/internal/settings"
"github.com/pingular/pingularity/internal/speedtest"
"github.com/pingular/pingularity/internal/store"
)
func seedWinners(t *testing.T, st *store.Store, base int64, reasons ...string) {
t.Helper()
for i, r := range reasons {
row := store.SpeedServerRow{RunTS: base + int64(i)*60, ServerID: "s" + string(rune('a'+i)), Server: "S", RankOrder: 1, Winner: true, WinReason: r}
if err := st.InsertSpeedServers(context.Background(), []store.SpeedServerRow{row}); err != nil {
t.Fatal(err)
}
}
}
// A challenge is due once N automatic winners have passed with no challenge
// among them; pinned runs neither count nor reset; 0 turns it off.
func TestChallengeFnIsDueEveryNAutoRunsFromHistory(t *testing.T) {
st, err := store.Open(":memory:")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { st.Close() })
set, err := settings.New(context.Background(), st, settings.Values{SpeedChallengeEvery: 3,
Latency: 5 * time.Second, Speed: time.Hour, Timeout: 2 * time.Second, DownAfter: 3, UpAfter: 2})
if err != nil {
t.Fatal(err)
}
fn := newChallengeFn(st, set)
if fn() {
t.Fatal("no history: nothing to challenge yet")
}
base := time.Now().Add(-2 * time.Hour).Unix()
seedWinners(t, st, base, "fastest_ranked", "incumbent")
if fn() {
t.Error("two auto runs, N=3: not due")
}
seedWinners(t, st, base+1000, speedtest.WinReasonPinned) // a pinned run does not count
if fn() {
t.Error("a pinned run must not count toward N")
}
seedWinners(t, st, base+2000, "incumbent")
if !fn() {
t.Error("three auto runs without a challenge: due")
}
seedWinners(t, st, base+3000, speedtest.WinReasonChallenger) // the challenge happened (and lost)
if fn() {
t.Error("just challenged: not due")
}
seedWinners(t, st, base+4000, "incumbent", "incumbent")
if fn() {
t.Error("two auto runs since the challenge: not due")
}
seedWinners(t, st, base+5000, "incumbent")
if !fn() {
t.Error("three auto runs since the challenge: due again")
}
// A pinned stretch longer than N must not start the count over.
seedWinners(t, st, base+6000, speedtest.WinReasonPinned, speedtest.WinReasonPinnedBestOf, speedtest.WinReasonPinnedCompanion, speedtest.WinReasonPinned)
if !fn() {
t.Error("four pinned runs in a row: still due - pinned runs neither count nor reset")
}
off, err := store.Open(":memory:")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { off.Close() })
setOff, err := settings.New(context.Background(), off, settings.Values{SpeedChallengeEvery: 0,
Latency: 5 * time.Second, Speed: time.Hour, Timeout: 2 * time.Second, DownAfter: 3, UpAfter: 2})
if err != nil {
t.Fatal(err)
}
seedWinners(t, off, base, "incumbent", "incumbent", "incumbent", "incumbent")
if newChallengeFn(off, setOff)() {
t.Error("0 = never challenge")
}
}
// The incumbent lookup skips a challenger that lost, and takes one that won.
func TestIncumbentFnSkipsLosingChallengers(t *testing.T) {
st, err := store.Open(":memory:")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { st.Close() })
fn := newIncumbentFn(st)
base := time.Now().Add(-time.Hour).Unix()
row := func(ts int64, id, reason string) store.SpeedServerRow {
return store.SpeedServerRow{RunTS: ts, ServerID: id, Server: "S", RankOrder: 1, Winner: true, WinReason: reason}
}
if err := st.InsertSpeedServers(context.Background(), []store.SpeedServerRow{row(base, "seat", "incumbent")}); err != nil {
t.Fatal(err)
}
if err := st.InsertSpeedServers(context.Background(), []store.SpeedServerRow{row(base+60, "loser", speedtest.WinReasonChallenger)}); err != nil {
t.Fatal(err)
}
if got := fn(); got != "seat" {
t.Errorf("incumbent %q, want seat: a challenger that lost must not take the seat", got)
}
if err := st.InsertSpeedServers(context.Background(), []store.SpeedServerRow{row(base+120, "winner", speedtest.WinReasonChallengerWon)}); err != nil {
t.Fatal(err)
}
if got := fn(); got != "winner" {
t.Errorf("incumbent %q, want winner: a challenger that won holds the seat", got)
}
}
func TestIncumbentScoresFnReadsTheRecord(t *testing.T) {
st, err := store.Open(":memory:")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { st.Close() })
base := time.Now().Add(-time.Hour).Unix()
rows := []store.SpeedServerRow{
{RunTS: base, ServerID: "1993", Server: "CalNect", RankOrder: 1, Measured: true, Winner: true, Score: 50, DownMbps: 100, UpMbps: 20},
{RunTS: base + 60, ServerID: "1993", Server: "CalNect", RankOrder: 1, Measured: true, Winner: true, Score: 60, DownMbps: 100, UpMbps: 20},
{RunTS: base + 120, ServerID: "1993", Server: "CalNect", RankOrder: 1, Measured: true, Winner: true, Score: 70, DownMbps: 100, UpMbps: 20},
// A download-only run: judged under another direction, not comparable to a "both" record.
{RunTS: base + 180, ServerID: "1993", Server: "CalNect", RankOrder: 1, Measured: true, Winner: true, Score: 90, DownMbps: 100},
// A partial run whose upload failed scores 0: not a record, and must not eat a window slot.
{RunTS: base + 240, ServerID: "1993", Server: "CalNect", RankOrder: 1, Measured: true, Winner: true, Score: 0, DownMbps: 100, UpMbps: 20},
{RunTS: base + 240, ServerID: "2", Server: "X", RankOrder: 2},
}
for _, r := range rows {
if err := st.InsertSpeedServers(context.Background(), []store.SpeedServerRow{r}); err != nil {
t.Fatal(err)
}
}
got := newIncumbentScoresFn(st)("1993", "both")
if len(got) != 3 || got[0] != 70 || got[2] != 50 {
t.Errorf("both: scores %v, want 70, 60, 50 newest first - the download-only and the zero rows excluded", got)
}
if got := newIncumbentScoresFn(st)("1993", "down"); len(got) != 1 || got[0] != 90 {
t.Errorf("down: scores %v, want the one download-only row", got)
}
if got := newIncumbentScoresFn(st)("2", "both"); len(got) != 0 {
t.Errorf("a server never measured has no record, got %v", got)
}
}
// The recent-winner origin: the newest decided race's city, as a candidate
// for the next race; nil with no such race.
func TestRecentWinnerFnNamesTheLastDecidedRace(t *testing.T) {
st, err := store.Open(":memory:")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { st.Close() })
fn := newRecentWinnerFn(st)
if fn() != nil {
t.Fatal("no history: no recent winner")
}
la, lo := 45.5017, -73.5673
if err := st.InsertSpeed(context.Background(), store.SpeedSample{TS: time.Now().Add(-time.Hour).Unix(), Server: "s", Engine: "ookla",
DownMbps: 1, UpMbps: 1, PingMS: 1, RaceOutcome: "decided", RaceWinnerKind: "exit", RaceWinnerLabel: "Montréal, CA",
RaceWinnerLat: &la, RaceWinnerLon: &lo}); err != nil {
t.Fatal(err)
}
got := fn()
if got == nil || got.Kind != "recent" || got.Label != "Montréal, CA" || got.Lat != la || got.Lon != lo || !got.Anchored {
t.Errorf("got %+v, want the Montréal race as an anchored 'recent' origin", got)
}
}