Ctrl K
ComunidadGuías…

challenge_wiring_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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)
	}
}