package oltp
import (
"errors"
"testing"
)
// A sync applies the categories and keeps everyone else as a list: no
// account is created, and a matching name is only suggested.
func TestFossilUsersAreAReferenceNotAccounts(t *testing.T) {
db := testDB(t)
if err := db.AddAccount("bob", "a long password", false); err != nil {
t.Fatal(err)
}
rows, cats, err := db.ReplaceFossilUsers("alpha", []FossilUser{
{Login: "developer", Caps: "ei"},
{Login: "alice", Caps: "ei", Info: "Alice Dev"},
{Login: "bob", Caps: "k"},
})
if err != nil {
t.Fatal(err)
}
if rows != 2 || cats != 1 {
t.Fatalf("rows %d, categories %d", rows, cats)
}
if got := db.CategoryCaps("alpha", "developer"); got != "ei" {
t.Fatalf("developer category = %q", got)
}
if _, ok := db.AccountByName("alice"); ok {
t.Fatal("a sync created an account")
}
users, err := db.FossilUsers("alpha")
if err != nil {
t.Fatal(err)
}
if len(users) != 2 || users[0].Login != "alice" || users[0].Suggest || !users[1].Suggest {
t.Fatalf("users = %+v", users)
}
if db.EffectiveCaps("bob", "alpha").HasAny("k") {
t.Fatal("a matching name was granted without a link")
}
}
// Only a link grants; a re-sync refreshes a linked account; a login
// that leaves fossil loses its row but not the account's access.
func TestFossilLinkGrantsAndSurvivesResync(t *testing.T) {
db := testDB(t)
if _, _, err := db.ReplaceFossilUsers("alpha", []FossilUser{{Login: "alice", Caps: "i"}}); err != nil {
t.Fatal(err)
}
if err := linkErr(db, "alpha", "alice", "al"); !errors.Is(err, ErrNoAccount) {
t.Fatalf("link to a missing account: %v", err)
}
if err := linkErr(db, "alpha", "nobody-here", ""); !errors.Is(err, ErrNoFossilUser) {
t.Fatalf("link of a missing login: %v", err)
}
if err := db.AddAccount("al", "a long password", false); err != nil {
t.Fatal(err)
}
if err := linkErr(db, "alpha", "alice", "al"); err != nil {
t.Fatal(err)
}
if !db.EffectiveCaps("al", "alpha").HasAny("i") {
t.Fatal("a link did not grant the login's capabilities")
}
if _, _, err := db.ReplaceFossilUsers("alpha", []FossilUser{{Login: "alice", Caps: "k"}}); err != nil {
t.Fatal(err)
}
users, _ := db.FossilUsers("alpha")
if len(users) != 1 || users[0].Linked != "al" || !db.EffectiveCaps("al", "alpha").HasAny("k") {
t.Fatalf("a re-sync lost the link or the refresh: %+v", users)
}
if _, _, err := db.ReplaceFossilUsers("alpha", nil); err != nil {
t.Fatal(err)
}
if users, _ := db.FossilUsers("alpha"); len(users) != 0 {
t.Fatalf("a login gone from fossil kept its row: %+v", users)
}
if !db.EffectiveCaps("al", "alpha").HasAny("k") {
t.Fatal("a sync revoked an account's access without an admin")
}
}
// linkErr links as an actor the guard has nothing to say about here —
// none of these links touches an admin — for the tests about what a
// link does rather than who may make it.
func linkErr(db *DB, repo, login, account string) error {
_, err := db.LinkFossilUser("root", repo, login, account)
return err
}