Skip to content
quarry Artifact [63c56cb6df]
Login

Artifact [63c56cb6df]

Artifact 63c56cb6df00f690cf08ca3bdf605e57cb522483799cbd97918f6313aa448537:


package oltp

import (
	"path/filepath"
	"testing"
	"time"
)

// reopen closes and reopens the registry at the same path, so the
// migration list runs a second time against rows already in it. That
// is the only honest way to exercise a backfill: the fresh schema
// already has the columns, so the UPDATE has to be what fills them.
func reopen(t *testing.T, db *DB, path string) *DB {
	t.Helper()
	if err := db.Close(); err != nil {
		t.Fatal(err)
	}
	next, err := Open(path)
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { next.Close() })
	return next
}

func TestOwnerAndNamespaceRoundTrip(t *testing.T) {
	db := testDB(t)
	if err := db.AddRepo("proj", t.TempDir(), "", Public); err != nil {
		t.Fatal(err)
	}
	owner, err := db.OwnerOf("proj")
	if err != nil {
		t.Fatal(err)
	}
	if owner != "" {
		t.Fatalf("a fresh repository starts forge-held, got owner %q", owner)
	}
	if err := db.SetOwner("proj", "ada"); err != nil {
		t.Fatal(err)
	}
	at := time.Date(2026, 9, 13, 1, 2, 3, 0, time.UTC)
	if err := db.SetNamespace("proj", "hoppers", "grace", at); err != nil {
		t.Fatal(err)
	}
	owner, err = db.OwnerOf("proj")
	if err != nil {
		t.Fatal(err)
	}
	ns, err := db.NamespaceOf("proj")
	if err != nil {
		t.Fatal(err)
	}
	// The deed and the appearance are separate facts: placing a
	// repository under a team must not change who holds it.
	if owner != "ada" || ns != "hoppers" {
		t.Fatalf("owner/namespace = %q/%q, want ada/hoppers", owner, ns)
	}
	by, when, err := db.NamespacePlacement("proj")
	if err != nil {
		t.Fatal(err)
	}
	if by != "grace" || when != "2026-09-13T01:02:03Z" {
		t.Fatalf("placement = %q at %q, want grace at 2026-09-13T01:02:03Z", by, when)
	}
}

func TestOwnerOfUnknownRepoIsEmptyNotAnError(t *testing.T) {
	db := testDB(t)
	owner, err := db.OwnerOf("nope")
	if err != nil || owner != "" {
		t.Fatalf("OwnerOf(unknown) = %q, %v; want empty and no error", owner, err)
	}
}

func TestBackfillAdoptsTheSoleAdmin(t *testing.T) {
	path := filepath.Join(t.TempDir(), "oltp.db")
	db, err := Open(path)
	if err != nil {
		t.Fatal(err)
	}
	if err := db.AddRepo("solo", t.TempDir(), "", Public); err != nil {
		t.Fatal(err)
	}
	if err := db.SetCaps("ada", "solo", "a"); err != nil {
		t.Fatal(err)
	}
	db = reopen(t, db, path)
	owner, err := db.OwnerOf("solo")
	if err != nil {
		t.Fatal(err)
	}
	if owner != "ada" {
		t.Fatalf("owner = %q, want ada — one admin is an unambiguous owner", owner)
	}
	ns, err := db.NamespaceOf("solo")
	if err != nil {
		t.Fatal(err)
	}
	if ns != "ada" {
		t.Fatalf("namespace = %q, want ada — with no team, a repository appears under its owner", ns)
	}
}

// The case this rule exists for. On the production forge the agent
// account that imported every mirror holds `a` on all of them, and the
// human owner was granted `a` afterwards. Any rule that picks one of
// two admins — earliest, latest, alphabetical — can hand the deed to
// the wrong account silently. Declining is the only safe answer.
func TestBackfillDeclinesWhenTwoAccountsHoldAdmin(t *testing.T) {
	path := filepath.Join(t.TempDir(), "oltp.db")
	db, err := Open(path)
	if err != nil {
		t.Fatal(err)
	}
	if err := db.AddRepo("shared", t.TempDir(), "", Public); err != nil {
		t.Fatal(err)
	}
	if err := db.SetCaps("importer", "shared", "a"); err != nil {
		t.Fatal(err)
	}
	if err := db.SetCaps("ada", "shared", "a"); err != nil {
		t.Fatal(err)
	}
	db = reopen(t, db, path)
	owner, err := db.OwnerOf("shared")
	if err != nil {
		t.Fatal(err)
	}
	if owner != "" {
		t.Fatalf("owner = %q, want empty — two admins is ambiguous and must be left to a person", owner)
	}
}

func TestBackfillIgnoresNonAdminGrants(t *testing.T) {
	path := filepath.Join(t.TempDir(), "oltp.db")
	db, err := Open(path)
	if err != nil {
		t.Fatal(err)
	}
	if err := db.AddRepo("readonly", t.TempDir(), "", Public); err != nil {
		t.Fatal(err)
	}
	if err := db.SetCaps("lou", "readonly", "u3bBC78"); err != nil {
		t.Fatal(err)
	}
	db = reopen(t, db, path)
	owner, err := db.OwnerOf("readonly")
	if err != nil {
		t.Fatal(err)
	}
	if owner != "" {
		t.Fatalf("owner = %q, want empty — a reader is not an owner", owner)
	}
}

func TestBackfillLeavesAnExistingOwnerAlone(t *testing.T) {
	path := filepath.Join(t.TempDir(), "oltp.db")
	db, err := Open(path)
	if err != nil {
		t.Fatal(err)
	}
	if err := db.AddRepo("held", t.TempDir(), "", Public); err != nil {
		t.Fatal(err)
	}
	if err := db.SetOwner("held", "ada"); err != nil {
		t.Fatal(err)
	}
	// A later admin grant to somebody else must not re-derive the deed.
	if err := db.SetCaps("grace", "held", "a"); err != nil {
		t.Fatal(err)
	}
	db = reopen(t, db, path)
	owner, err := db.OwnerOf("held")
	if err != nil {
		t.Fatal(err)
	}
	if owner != "ada" {
		t.Fatalf("owner = %q, want ada — the backfill adopts only the unclaimed", owner)
	}
}

func TestReposOwnedByIsOwnershipNotReach(t *testing.T) {
	db := testDB(t)
	for _, n := range []string{"one", "two", "other"} {
		if err := db.AddRepo(n, t.TempDir(), "", Public); err != nil {
			t.Fatal(err)
		}
	}
	if err := db.SetOwner("one", "ada"); err != nil {
		t.Fatal(err)
	}
	if err := db.SetOwner("two", "ada"); err != nil {
		t.Fatal(err)
	}
	if err := db.SetOwner("other", "grace"); err != nil {
		t.Fatal(err)
	}
	// Admin on somebody else's repository is reach, not ownership.
	if err := db.SetCaps("ada", "other", "a"); err != nil {
		t.Fatal(err)
	}
	got, err := db.ReposOwnedBy("ada")
	if err != nil {
		t.Fatal(err)
	}
	if len(got) != 2 || got[0] != "one" || got[1] != "two" {
		t.Fatalf("ReposOwnedBy(ada) = %v, want [one two]", got)
	}
	n, err := db.OwnedRepoCount("ada")
	if err != nil {
		t.Fatal(err)
	}
	if n != 2 {
		t.Fatalf("OwnedRepoCount(ada) = %d, want 2 — the quota counts what you own, not what you can reach", n)
	}
}

// Load-bearing for the team union in EffectiveCaps. Team capabilities
// are resolved by matching team_member.team against the repository's
// namespace, so if a team could take a name an account already holds,
// a personal namespace would start matching a team's grants. The two
// name kinds share one pool and refuse each other in both directions;
// this asserts that rather than trusting it.
func TestTeamAndAccountNamesCannotCollide(t *testing.T) {
	db := testDB(t)
	if err := db.AddAccount("ada", "correct horse battery staple", false); err != nil {
		t.Fatal(err)
	}
	if err := db.AddTeam("hoppers", "Hoppers", "", "ada"); err != nil {
		t.Fatal(err)
	}
	if err := db.AddTeam("ada", "Not Ada", "", "ada"); err == nil {
		t.Fatal("a team took a name an account already holds; a personal namespace would then match team grants")
	}
	if err := db.AddAccount("hoppers", "correct horse battery staple", false); err == nil {
		t.Fatal("an account took a name a team already holds; the same collision, from the other side")
	}
}