package oltp
import "testing"
// The gate that let a stranger write. A repository created through
// repo.add has no sync users, and "no sync users" used to mean "anyone
// may push" โ so every fresh public repository on an internet-facing
// forge accepted a check-in from nobody at all. The letters decide now.
func TestNoSyncUsersFallsBackToTheLetters(t *testing.T) {
db := testDB(t)
// Stock: a signed-out visitor holds the nobody category, which
// carries no i. No sync users, and still gated.
if !db.Gated("fresh") {
t.Error("a fresh repository accepted an unauthenticated push")
}
if eff := db.EffectiveCaps("", "fresh"); eff.HasAny("i") {
t.Fatalf("premise broken: a signed-out visitor already holds i (%q)", eff)
}
}
// The opt-in still works, so a single-user local forge is not broken by
// closing the default. This is the case RFC 0001 ยง7 wanted; it just
// has to be asked for now.
func TestAnonymousPushIsAnOptInThroughTheLetters(t *testing.T) {
db := testDB(t)
if err := db.SetCategoryCaps("local", "nobody", "i"); err != nil {
t.Fatal(err)
}
if db.Gated("local") {
t.Error("a repository whose nobody category grants i still refused an anonymous push")
}
}
// i reached through a category pull-in counts, because the gate asks
// the EXPANDED set. A raw string match would miss this and refuse a
// push the settings page says is allowed.
func TestInheritedCheckInOpensTheGate(t *testing.T) {
db := testDB(t)
// v pulls in the developer category; put i only there.
if err := db.SetCategoryCaps("inherit", "nobody", "v"); err != nil {
t.Fatal(err)
}
if err := db.SetCategoryCaps("inherit", "developer", "i"); err != nil {
t.Fatal(err)
}
if !db.EffectiveCaps("", "inherit").HasAny("i") {
t.Skip("this build does not expand v for a signed-out visitor")
}
if db.Gated("inherit") {
t.Error("i inherited through v did not open the gate")
}
}
// A sync user still decides it, letters or not: naming a pusher is the
// older and more explicit statement of intent.
func TestSyncUsersStillGate(t *testing.T) {
db := testDB(t)
if err := db.SetCategoryCaps("named", "nobody", "i"); err != nil {
t.Fatal(err)
}
if db.Gated("named") {
t.Fatal("premise broken: the letters should have opened this")
}
if err := db.SetSyncUser("named", "dan", "secret"); err != nil {
t.Fatal(err)
}
if !db.Gated("named") {
t.Error("a repository with a sync user accepted an unauthenticated push")
}
}
// A gate that cannot read its own table must refuse, not wave the
// caller through. This returned false โ open โ on any query error.
func TestGateFailsClosedWhenItCannotAsk(t *testing.T) {
db := testDB(t)
if err := db.SetCategoryCaps("broken", "nobody", "i"); err != nil {
t.Fatal(err)
}
if db.Gated("broken") {
t.Fatal("premise broken: this should be open before the database goes away")
}
// Close the handle underneath it: every query now errors.
if err := db.db.Close(); err != nil {
t.Fatal(err)
}
if !db.Gated("broken") {
t.Error("a database that could not answer granted the write")
}
}