package oltp
import "testing"
// RFC 0026 steps 4 and 5: a repository's or a team's own agent holds
// what it was granted, inside its scope only, and never the flag.
func TestARepositoryAgentHoldsItsGrantThereOnly(t *testing.T) {
db := agentRig(t)
if err := db.NewDedicatedAgent("priv-ci", "repo:priv", "dmestas"); err != nil {
t.Fatal(err)
}
if db.EffectiveCaps("priv-ci", "priv").HasAny("o") {
t.Errorf("FAIL: a new repository agent holds something before a grant")
}
if err := db.Grant("priv-ci", "priv", Write); err != nil {
t.Fatal(err)
}
if !db.EffectiveCaps("priv-ci", "priv").HasAny("i") {
t.Errorf("FAIL: a repository agent granted developer cannot check in")
}
// A grant elsewhere does nothing: it is the repository's agent.
if err := db.SetRepoVisibility("pub", Private); err != nil {
t.Fatal(err)
}
if err := db.Grant("priv-ci", "pub", Write); err != nil {
t.Fatal(err)
}
if got := db.EffectiveCaps("priv-ci", "pub"); got.HasAny("o") {
t.Errorf("FAIL: a repository agent reaches another repository: %q", letters(got))
}
t.Log("ok: repository agent")
// It goes with its repository: disabled, tokens revoked, and no
// later repository of the name owns it.
if _, _, err := db.MintToken("priv-ci", TokenSpec{Name: "ci", Scopes: []string{ScopeAPI}}); err != nil {
t.Fatal(err)
}
if err := db.PurgeRepo("priv"); err != nil {
t.Fatal(err)
}
a, _ := db.AccountByName("priv-ci")
if a.Disabled == "" || a.Operator == "repo:priv" {
t.Errorf("FAIL: after its repository was deleted the agent is disabled=%q operator=%q", a.Disabled, a.Operator)
}
toks, _ := db.Tokens("priv-ci")
for _, tk := range toks {
if tk.Revoked == "" {
t.Errorf("FAIL: a deleted repository's agent kept a live token")
}
}
t.Log("ok: goes with its repository")
}
func TestATeamAgentReachesItsTeamsRepositories(t *testing.T) {
db := agentRig(t)
if err := db.AddTeam("crew", "", "", "dmestas"); err != nil {
t.Fatal(err)
}
if err := db.SetTeamRepo("crew", "priv", "dmestas"); err != nil {
t.Fatal(err)
}
if err := db.NewDedicatedAgent("crew-ci", "team:crew", "dmestas"); err != nil {
t.Fatal(err)
}
if err := db.SetTeamMember("crew", "crew-ci", Write.Caps(), false, true); err != nil {
t.Fatal(err)
}
if !db.EffectiveCaps("crew-ci", "priv").HasAny("i") {
t.Errorf("FAIL: a team agent added as a developer member cannot check in on the team's repository")
}
if err := db.SetRepoVisibility("pub", Private); err != nil {
t.Fatal(err)
}
if err := db.Grant("crew-ci", "pub", Write); err != nil {
t.Fatal(err)
}
if got := db.EffectiveCaps("crew-ci", "pub"); got.HasAny("o") {
t.Errorf("FAIL: a team agent reaches a repository outside its team: %q", letters(got))
}
t.Log("ok: team agent")
}
func TestADedicatedAgentNeedsARealScopeAndHasACap(t *testing.T) {
db := agentRig(t)
for _, s := range []string{"repo:nope", "team:nope", "site", ""} {
if err := db.NewDedicatedAgent("x-ci", s, "dmestas"); err == nil {
t.Errorf("FAIL: scope %q was accepted", s)
}
}
for i := 0; i < DedicatedAgentsPer; i++ {
if err := db.NewDedicatedAgent("ci-"+string(rune('a'+i)), "repo:pub", "dmestas"); err != nil {
t.Fatal(err)
}
}
if err := db.NewDedicatedAgent("ci-over", "repo:pub", "dmestas"); err == nil {
t.Errorf("FAIL: a repository took more than %d agents", DedicatedAgentsPer)
}
t.Log("ok: scope and cap")
}
// RFC 0026 review: a repository's agent never holds a, nor a deed, and
// a new operator never inherits an old opt-in.
func TestADedicatedAgentHoldsNoAdminAndNoDeed(t *testing.T) {
db := agentRig(t)
if err := db.NewDedicatedAgent("pub-ci", "repo:pub", "dmestas"); err != nil {
t.Fatal(err)
}
if err := db.Grant("pub-ci", "pub", RepoAdmin); err != nil {
t.Fatal(err)
}
if got := db.EffectiveCaps("pub-ci", "pub"); got.HasAny("a") || !got.HasAny("i") {
t.Errorf("FAIL: a repository agent granted Admin holds %q: want what Admin implies, without a", letters(got))
}
if err := db.SetOwner("pub", "pub-ci"); err == nil {
t.Errorf("FAIL: a repository agent took the deed")
}
t.Log("ok: no a, no deed")
}
func TestANewOperatorNeverInheritsTheOptIn(t *testing.T) {
db := agentRig(t)
if err := db.NewAgent("root-bot", "root", -1); err != nil {
t.Fatal(err)
}
if err := db.SetSiteOpt("root", "root-bot"); err != nil {
t.Fatal(err)
}
if err := db.AddAccount("root2", "a-long-enough-password", true); err != nil {
t.Fatal(err)
}
if err := db.SetOperator("root-bot", "root2"); err != nil {
t.Fatal(err)
}
if db.InheritsSiteAdmin("root-bot") {
t.Errorf("FAIL: the agent inherited its new operator's flag without their opt-in")
}
t.Log("ok: the opt-in stays with the person who chose it")
}
// jva, ^2642: a team agent whose member role is Admin holds Maintainer
// on the team's repositories, as a repository agent granted Admin does.
func TestATeamAgentWithAdminRoleHoldsMaintainer(t *testing.T) {
db := agentRig(t)
if err := db.AddTeam("crew", "", "", "dmestas"); err != nil {
t.Fatal(err)
}
if err := db.SetTeamRepo("crew", "priv", "dmestas"); err != nil {
t.Fatal(err)
}
if err := db.NewDedicatedAgent("crew-ci", "team:crew", "dmestas"); err != nil {
t.Fatal(err)
}
if err := db.SetTeamMember("crew", "crew-ci", RepoAdmin.Caps(), false, true); err != nil {
t.Fatal(err)
}
if got := db.EffectiveCaps("crew-ci", "priv"); got.HasAny("a") || !got.HasAny("i") {
t.Errorf("FAIL: a team agent with member role Admin holds %q, want Maintainer without a", letters(got))
}
t.Log("ok: team Admin is Maintainer")
}