Ctrl K
ComunidadGuías…

firstrun_wiring_test.go

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

Archivo original ↗Preguntar a KiBot
package main

import (
	"context"
	"io"
	"log/slog"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/pingular/pingularity/internal/netinfo"
	"github.com/pingular/pingularity/internal/settings"
	"github.com/pingular/pingularity/internal/speedtest"
	"github.com/pingular/pingularity/internal/store"
)

// The production assembly IS the feature: package tests cover injected hooks,
// but deleting `ni.WakeFn = set.Changed` (or miswiring it) would leave every
// package test green while the first speedtest quietly went back to racing an
// empty netinfo. This drives the REAL settings controller's close-and-replace
// broadcast into the REAL netinfo loop, wired exactly as main.go wires them.
func TestFirstRunWiringSettingsBroadcastWakesNetinfo(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	st, err := store.Open(":memory:")
	if err != nil {
		t.Fatalf("open store: %v", err)
	}
	defer st.Close()
	set, err := settings.New(ctx, st, settings.Values{Monitoring: false, NetinfoEnabled: true})
	if err != nil {
		t.Fatalf("settings: %v", err)
	}

	ni := netinfo.NewManager(slog.New(slog.NewTextHandler(io.Discard, nil)))
	ni.EnabledFn = func() bool { return set.Monitoring() && set.NetinfoEnabled() }
	ni.WakeFn = set.Changed // the production wiring under test (main.go)

	loopCtx, loopCancel := context.WithCancel(ctx)
	done := make(chan struct{})
	defer func() { loopCancel(); <-done }()
	go func() { defer close(done); ni.Loop(loopCtx, time.Hour) }()

	time.Sleep(150 * time.Millisecond)
	if ni.Get().UpdatedAt != 0 {
		t.Fatal("lookup attempted while monitoring was off")
	}

	// The power button. A real mutate -> a real broadcast -> the loop must
	// attempt a refresh well inside its one-minute disabled poll.
	if err := set.SetMonitoring(ctx, true); err != nil {
		t.Fatalf("SetMonitoring: %v", err)
	}
	deadline := time.Now().Add(15 * time.Second)
	for ni.Get().UpdatedAt == 0 {
		if time.Now().After(deadline) {
			t.Fatal("power-on broadcast never reached the netinfo loop; the ni.WakeFn = set.Changed wiring is dead")
		}
		time.Sleep(20 * time.Millisecond)
	}
}

// newFirstRunReadyFn's truth table, against the real controller and manager -
// this is the composition main.go hands to the scheduler, so a reordered or
// dropped short-circuit here is a 20-second stall (or a defeated wait) on
// every fresh install's first test.
func TestFirstRunReadyFnComposition(t *testing.T) {
	ctx := context.Background()
	mk := func(v settings.Values) (*settings.Controller, *netinfo.Manager) {
		st, err := store.Open(":memory:")
		if err != nil {
			t.Fatalf("open store: %v", err)
		}
		t.Cleanup(func() { st.Close() })
		set, err := settings.New(ctx, st, v)
		if err != nil {
			t.Fatalf("settings: %v", err)
		}
		return set, netinfo.NewManager(slog.New(slog.NewTextHandler(io.Discard, nil)))
	}

	// Fresh install, netinfo on, nothing published: not ready (the wait is the
	// feature - without it the first run races an empty netinfo).
	set, ni := mk(settings.Values{NetinfoEnabled: true})
	if newFirstRunReadyFn(set, ni)() {
		t.Error("fresh install with netinfo pending must NOT be ready")
	}

	// Pinned server: the race is irrelevant.
	set, ni = mk(settings.Values{NetinfoEnabled: true, SpeedServerID: "12345"})
	if !newFirstRunReadyFn(set, ni)() {
		t.Error("pinned server must be ready immediately")
	}

	// Connection info off: nothing is ever coming.
	set, ni = mk(settings.Values{NetinfoEnabled: false})
	if !newFirstRunReadyFn(set, ni)() {
		t.Error("netinfo disabled must be ready immediately")
	}

	// iperf3 engine: ready only when the binary is actually usable - an
	// unavailable iperf3 falls back to Ookla, which DOES need the race.
	set, ni = mk(settings.Values{NetinfoEnabled: true, SpeedEngine: "iperf3"})
	if got, want := newFirstRunReadyFn(set, ni)(), speedtest.IperfAvailable(); got != want {
		t.Errorf("iperf3 engine readiness = %v, want %v (IperfAvailable on this host)", got, want)
	}
}

// The pending->PUBLISHED half of the readiness truth table. TestFirstRunReadyFnComposition
// only pins the pending (false) side, so a mutation that leaves readiness
// permanently false AFTER netinfo publishes slips past it. This drives a real
// publish (even a failed lookup stamps UpdatedAt) and asserts readiness flips.
func TestFirstRunReadyFnReleasesAfterPublish(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	st, err := store.Open(":memory:")
	if err != nil {
		t.Fatalf("open store: %v", err)
	}
	defer st.Close()
	set, err := settings.New(ctx, st, settings.Values{Monitoring: true, NetinfoEnabled: true})
	if err != nil {
		t.Fatalf("settings: %v", err)
	}
	ni := netinfo.NewManager(slog.New(slog.NewTextHandler(io.Discard, nil)))
	ni.EnabledFn = func() bool { return set.Monitoring() && set.NetinfoEnabled() }
	ready := newFirstRunReadyFn(set, ni)

	if ready() {
		t.Fatal("must NOT be ready before netinfo publishes (UpdatedAt still 0)")
	}
	loopCtx, loopCancel := context.WithCancel(ctx)
	done := make(chan struct{})
	defer func() { loopCancel(); <-done }()
	go func() { defer close(done); ni.Loop(loopCtx, time.Hour) }()

	deadline := time.Now().Add(15 * time.Second)
	for !ready() {
		if time.Now().After(deadline) {
			t.Fatalf("readiness never released after netinfo published (UpdatedAt=%d)", ni.Get().UpdatedAt)
		}
		time.Sleep(20 * time.Millisecond)
	}
}

// The wiring lives in main.go's run() assembly, which isn't unit-testable, and
// the tests above re-create it locally - so deleting it from main.go leaves them
// green (the exact gap that shipped). This source-presence guard fails if the
// production wiring is removed or renamed. It is deliberately literal.
func TestMainWiresFirstRunAndOptsHooks(t *testing.T) {
	src, err := os.ReadFile("main.go")
	if err != nil {
		t.Fatal(err)
	}
	for _, want := range []string{
		"ni.WakeFn = set.Changed",                     // #3: first speedtest must not race an empty netinfo
		"sched.ReadyFn = newFirstRunReadyFn(set, ni)", // #3: selection readiness predicate
		"replayIgnoredOpts(",                          // #7: ignored-PINGULARITY_OPTS warning is replayed at boot
		// Record failed tests: without this line the switch saves, shows and
		// round-trips, and no failed run is ever recorded.
		"sched.RecordFailuresFn = set.SpeedtestRecordFailures",
	} {
		if !strings.Contains(string(src), want) {
			t.Errorf("main.go no longer wires %q - package tests won't catch it; the behavior is silently dead", want)
		}
	}
}