Ctrl K
ComunidadGuías…

birthmarker_creation_test.go

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

Archivo original ↗Preguntar a KiBot
package main

import (
	"context"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"runtime"
	"testing"
	"time"

	"github.com/pingular/pingularity/internal/config"
	"github.com/pingular/pingularity/internal/settings"
	"github.com/pingular/pingularity/internal/store"
)

// The birth marker (settings.KeyInstallBornVersion) is forward provenance: its
// presence tells warnAmbiguousContainerAccess that a store's missing access key
// means "never chose", not "was reachable before the upgrade". Settings can only
// read the store's contents, where an install stopped before it ever measured
// looks exactly like a brand-new one, so main supplies the missing half: whether
// the database file was there before it opened it.

// A container upgraded from a release whose dashboard answered the network,
// whose database was created back then and never written to: it must not be
// stamped, and once it starts measuring it must get the warning naming its 403.
func TestUpgradedEmptyContainerStoreIsNotStampedAndStaysExplained(t *testing.T) {
	ctx := context.Background()
	dbPath := filepath.Join(t.TempDir(), "pingularity.db")

	// The 0.61 era: the daemon ran once, created the database, and was stopped at
	// the consent prompt. The offer clock is all it left behind.
	old, err := store.Open(dbPath)
	if err != nil {
		t.Fatalf("open the 0.61 database: %v", err)
	}
	if err := old.SetSetting(ctx, "quick_setup_offer_since", "1754400000"); err != nil {
		t.Fatalf("seed the first-run offer clock: %v", err)
	}
	old.Close()

	// The upgrade boot, taking main's own reading of the path before the open.
	created := dbCreatedNow(dbPath)
	if created {
		t.Fatalf("precondition: %s exists, so this boot did not create it; the test proves nothing if main thinks otherwise", dbPath)
	}
	st, err := store.Open(dbPath)
	if err != nil {
		t.Fatalf("reopen on the new release: %v", err)
	}
	defer st.Close()
	set, err := settings.New(ctx, st, testDefaultsFor(config.Config{}),
		settings.WithBornVersion("0.80.0-test"), settings.WithDatabaseCreated(created))
	if err != nil {
		t.Fatalf("settings.New: %v", err)
	}
	if v, marked := settingsSnapshot(t, st)[settings.KeyInstallBornVersion]; marked {
		t.Fatalf("the upgrade stamped %s=%q on a database an earlier release created. The marker claims this build watched the store come into existence, and downstream that claim is what stops the daemon explaining a 403 to the operator whose published port used to answer.", settings.KeyInstallBornVersion, v)
	}

	// History is what makes the store established, the shape the warning aims at.
	if err := st.InsertSamples(ctx, []store.Sample{
		{TS: time.Now().Add(-2 * time.Hour), Target: "cloudflare", Family: "ipv4", LatencyMS: 11, Success: true},
	}); err != nil {
		t.Fatalf("seed history: %v", err)
	}

	warns := bootAccessDecision(t, config.Config{}, st, set, true)
	if len(warns) != 1 || warns[0].msg != accessAmbiguousWarnMsg {
		t.Fatalf("got %d warnings (%v), want exactly the ambiguity warning. This install's dashboard did answer the network before the upgrade; without this line the operator sees only a 403 on a port that worked yesterday, with nothing in the log naming the cause or the fix.", len(warns), warns)
	}
	if set.AccessLocalOnly() != true || storedAccessKey(t, st) {
		t.Fatal("the boot decided or persisted access; the warning explains, it never writes")
	}
}

// The other population: a genuinely new install must still get stamped, or every
// fresh container would collect the ambiguity warning on every boot.
func TestFreshInstallIsStampedAndNeverWarned(t *testing.T) {
	ctx := context.Background()
	dbPath := filepath.Join(t.TempDir(), "pingularity.db")

	created := dbCreatedNow(dbPath) // nothing there yet: this boot is the birth
	if !created {
		t.Fatalf("precondition: %s must not exist before the first boot", dbPath)
	}
	st, err := store.Open(dbPath)
	if err != nil {
		t.Fatalf("open: %v", err)
	}
	defer st.Close()
	set, err := settings.New(ctx, st, testDefaultsFor(config.Config{}),
		settings.WithBornVersion("0.80.0-test"), settings.WithDatabaseCreated(created))
	if err != nil {
		t.Fatalf("settings.New: %v", err)
	}
	if v, marked := settingsSnapshot(t, st)[settings.KeyInstallBornVersion]; !marked || v != "0.80.0-test" {
		t.Fatalf("marker = %q, %v on an install this boot really did create; want it stamped. Its absence is what makes an install look ambiguous forever.", v, marked)
	}
	if err := set.BornMarkerErr(); err != nil {
		t.Fatalf("BornMarkerErr = %v on a boot that created the database and could write to it", err)
	}

	// Established too, like the upgrade above, but born private: still silent.
	if err := st.InsertSamples(ctx, []store.Sample{
		{TS: time.Now().Add(-2 * time.Hour), Target: "cloudflare", Family: "ipv4", LatencyMS: 11, Success: true},
	}); err != nil {
		t.Fatalf("seed history: %v", err)
	}
	if warns := bootAccessDecision(t, config.Config{}, st, set, true); len(warns) != 0 {
		t.Fatalf("got %d warnings (%v) on an install born under the fail-closed default; its marker says its missing access key means \"never chose\", so there is nothing ambiguous to report", len(warns), warns)
	}
}

// `pingularity reset-auth` refuses to create a database, so it can never be the
// process that witnessed a birth and must record none.
func TestResetAuthNeverStampsABirthMarker(t *testing.T) {
	dbPath := filepath.Join(t.TempDir(), "pingularity.db")
	old, err := store.Open(dbPath)
	if err != nil {
		t.Fatalf("open: %v", err)
	}
	old.Close()

	if err := resetAuthCmd([]string{"-db", dbPath}); err != nil {
		t.Fatalf("resetAuthCmd: %v", err)
	}

	st, err := store.Open(dbPath)
	if err != nil {
		t.Fatalf("reopen: %v", err)
	}
	defer st.Close()
	if v, marked := settingsSnapshot(t, st)[settings.KeyInstallBornVersion]; marked {
		t.Fatalf("reset-auth stamped %s=%q; a command that refuses to create a database has witnessed no birth and must record none", settings.KeyInstallBornVersion, v)
	}
}

// Only a definite "the path is not there" may answer yes: an unproven birth
// left unrecorded costs a warning, while a birth recorded that never happened
// silences one.
func TestDbCreatedNowOnlyAnswersYesToADefiniteAbsence(t *testing.T) {
	dir := t.TempDir()

	existing := filepath.Join(dir, "pingularity.db")
	if err := os.WriteFile(existing, []byte("not really sqlite"), 0o600); err != nil {
		t.Fatalf("seed an existing database: %v", err)
	}
	asDir := filepath.Join(dir, "mounted-over")
	if err := os.Mkdir(asDir, 0o700); err != nil {
		t.Fatalf("seed a directory at the db path: %v", err)
	}

	cases := []struct {
		name string
		path string
		want bool
		why  string
	}{
		{"nothing at the path", filepath.Join(dir, "brand-new.db"), true,
			"a boot that finds no database is the one boot that creates it; refusing here leaves every fresh install unmarked and permanently ambiguous"},
		{"an existing database", existing, false,
			"an earlier release created this file - the exact case the marker must not claim"},
		{"a directory at the path", asDir, false,
			"a volume mounted over the database path is not a birth, and stat succeeds here so an err != nil test would not catch it"},
	}
	if runtime.GOOS != "windows" {
		// A path under a regular file: the stat fails for a reason that is not
		// absence - except on Windows, which reports it as not found, so skip there.
		cases = append(cases, struct {
			name string
			path string
			want bool
			why  string
		}{"a stat that cannot answer", filepath.Join(existing, "pingularity.db"), false,
			"the stat failed for a reason that is not absence, so nothing was witnessed; answering yes on any error at all would mint provenance out of a failed check"})
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			if got := dbCreatedNow(c.path); got != c.want {
				t.Errorf("dbCreatedNow(%q) = %v, want %v: %s", c.path, got, c.want, c.why)
			}
		})
	}
}

// run()'s boot sequence is not callable from a test, so this checks the wiring
// in the source, as TestOnlyExplicitInputPersistsAccess does: every function in
// main.go that builds a controller must also pass the creation verdict, or
// settings silently falls back to the emptiness reading that stamped upgrades as
// newly born. Per-function rather than per-argument because run() spreads an
// options slice, where an argument-level check would find nothing to look at.
func TestMainAlwaysTellsSettingsWhetherItCreatedTheDatabase(t *testing.T) {
	const verdict = "WithDatabaseCreated"

	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, "main.go", nil, 0)
	if err != nil {
		t.Fatalf("parse main.go: %v", err)
	}

	// settingsCall reports whether body calls settings.<name> anywhere inside it.
	settingsCall := func(body *ast.BlockStmt, name string) (token.Pos, bool) {
		var at token.Pos
		ast.Inspect(body, func(n ast.Node) bool {
			call, ok := n.(*ast.CallExpr)
			if !ok || at.IsValid() {
				return !at.IsValid()
			}
			sel, ok := call.Fun.(*ast.SelectorExpr)
			if !ok || sel.Sel.Name != name {
				return true
			}
			if pkg, ok := sel.X.(*ast.Ident); ok && pkg.Name == "settings" {
				at = call.Pos()
				return false
			}
			return true
		})
		return at, at.IsValid()
	}

	builders := 0
	for _, decl := range file.Decls {
		fn, ok := decl.(*ast.FuncDecl)
		if !ok || fn.Body == nil {
			continue
		}
		at, builds := settingsCall(fn.Body, "New")
		if !builds {
			continue
		}
		builders++
		if _, told := settingsCall(fn.Body, verdict); !told {
			t.Errorf("%s: %s builds a settings controller without settings.%s; main is the only layer that can see whether the database file existed, and without that verdict settings falls back to reading the store as empty - which an install stopped before its first measurement satisfies just as well as a brand-new one",
				fset.Position(at), fn.Name.Name, verdict)
		}
	}
	if builders == 0 {
		t.Fatal("no settings.New call found in main.go; this test is checking nothing")
	}
}