package api
import (
"context"
"errors"
"path/filepath"
"testing"
"craftdesign.group/quarry/internal/oltp"
"craftdesign.group/quarry/internal/ops"
)
func agentRig(t *testing.T) (*ops.Registry, *oltp.DB) {
t.Helper()
db, err := oltp.Open(filepath.Join(t.TempDir(), "o.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { db.Close() })
for _, who := range []string{"dmestas", "jva", "zeitgeist", "mallory"} {
if err := db.AddAccount(who, "a-long-enough-password", who == "dmestas"); err != nil {
t.Fatal(err)
}
}
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: testRepos(wikiIndex(t)),
Teams: db, Accounts: acctAdapter{db},
CapsFor: func(a, repo string) CapSet { return db.EffectiveCaps(a, repo) }}); err != nil {
t.Fatal(err)
}
return r, db
}
// Marking is a site admin's act in both directions (RFC 0021): an
// operator is a person who can be asked, so an account naming itself
// is refused by the store, and a person makes agents with agent.new.
func TestOnlyASiteAdminMarksOrClearsAnAgent(t *testing.T) {
r, db := agentRig(t)
as := func(who string) context.Context { return ops.WithUser(context.Background(), who) }
if _, err := r.Invoke(as("zeitgeist"), "account.agent",
accountAgentIn{Name: "zeitgeist", Operator: "zeitgeist"}); !errors.Is(err, ops.ErrForbidden) {
t.Errorf("FAIL: an account marked itself: %v", err)
} else {
t.Log("ok: an account may not mark itself")
}
if _, err := r.Invoke(as("mallory"), "account.agent",
accountAgentIn{Name: "jva", Operator: "mallory"}); !errors.Is(err, ops.ErrForbidden) {
t.Errorf("FAIL: a stranger marked another account: %v", err)
} else {
t.Log("ok: marking somebody else needs an admin")
}
if _, err := r.Invoke(as("dmestas"), "account.agent",
accountAgentIn{Name: "jva", Operator: "jva"}); !errors.Is(err, ops.ErrInvalid) {
t.Errorf("FAIL: an account became its own operator: %v", err)
} else {
t.Log("ok: an agent is never its own operator")
}
if _, err := r.Invoke(as("dmestas"), "account.agent",
accountAgentIn{Name: "jva", Operator: "dmestas"}); err != nil {
t.Fatalf("FAIL: the admin could not mark jva: %v", err)
}
if db.Operator("jva") != "dmestas" {
t.Error("FAIL: the admin's mark did not stick")
} else {
t.Log("ok: an admin may mark another account, naming who answers for it")
}
if _, err := r.Invoke(as("jva"), "account.agent", accountAgentIn{Name: "jva"}); !errors.Is(err, ops.ErrForbidden) {
t.Errorf("FAIL: an agent unmarked itself: %v", err)
}
if _, err := r.Invoke(as("dmestas"), "account.agent", accountAgentIn{Name: "jva"}); err != nil {
t.Fatalf("FAIL: the admin could not clear the mark: %v", err)
}
if db.Operator("jva") != "" {
t.Error("FAIL: the admin's clear did not stick")
} else {
t.Log("ok: an admin may clear a mark")
}
}
// An operator nobody can be asked about is the thing this exists to
// end, so the name has to be an account.
func TestAnOperatorMustBeSomebodyYouCanAsk(t *testing.T) {
r, _ := agentRig(t)
_, err := r.Invoke(ops.WithUser(context.Background(), "dmestas"), "account.agent",
accountAgentIn{Name: "jva", Operator: "nobody-here"})
if err == nil {
t.Error("FAIL: an operator who is not an account was accepted")
} else {
t.Logf("ok: refused — %v", err)
}
}