Skip to content
quarry Artifact [56e0697599]
Login

Artifact [56e0697599]

Artifact 56e069759957020e9f09fc2cbba83b6c061709fdf1a33d2417366c348cc424ee:


package oltp

import "testing"

// RFC 0018's one screen asks "who reaches these repositories", so a
// non-member holding a string on one of them belongs on it. An answer
// that listed only members would be answering a different question.
func TestTeamPeopleIncludesNonMembersWhoReachIt(t *testing.T) {
	db := teamDB(t)
	if err := db.AddTeam("core", "", "", "dan"); err != nil {
		t.Fatal(err)
	}
	if err := db.SetTeamRepo("core", "quarry", "dan"); err != nil {
		t.Fatal(err)
	}
	if err := db.SetTeamRepo("core", "zit", "dan"); err != nil {
		t.Fatal(err)
	}
	// lou is a member with the floor alone.
	if err := db.SetTeamMember("core", "lou", "", false, true); err != nil {
		t.Fatal(err)
	}
	// ann is in no team but holds a string on one of the repositories.
	if err := db.SetCaps("ann", "zit", "a"); err != nil {
		t.Fatal(err)
	}
	// And a grant on a repository the team does NOT hold must not pull
	// somebody onto this screen.
	if err := db.SetCaps("ann", "elsewhere", "a"); err != nil {
		t.Fatal(err)
	}

	people, err := db.TeamPeople("core")
	if err != nil {
		t.Fatal(err)
	}
	got := map[string]TeamPerson{}
	for _, p := range people {
		got[p.Account] = p
	}
	if len(people) != 3 {
		t.Fatalf("FAIL: %d people, want dan, lou and ann: %+v", len(people), people)
	}
	if !got["dan"].Owner {
		t.Error("FAIL: the creator does not read as owner")
	}
	if !got["lou"].Member || got["lou"].Caps != "" {
		t.Errorf("FAIL: lou = %+v, want a member holding the floor alone", got["lou"])
	}
	ann := got["ann"]
	if ann.Member {
		t.Error("FAIL: ann reads as a member of a team she is not in")
	}
	if len(ann.Direct) != 1 || ann.Direct[0].Repo != "zit" {
		t.Errorf("FAIL: ann's direct grants = %+v, want zit only — elsewhere is not this team's", ann.Direct)
	}
	if !ann.Reaches() {
		t.Error("FAIL: ann does not read as reaching anything")
	}
}

// Owners first, then members, then everybody else, each by name: the
// order somebody scanning for "who runs this" needs.
func TestTeamPeopleAreOrderedByStanding(t *testing.T) {
	db := teamDB(t)
	if err := db.AddTeam("core", "", "", "lou"); err != nil {
		t.Fatal(err)
	}
	if err := db.SetTeamRepo("core", "quarry", "lou"); err != nil {
		t.Fatal(err)
	}
	if err := db.SetTeamMember("core", "ann", "", false, true); err != nil {
		t.Fatal(err)
	}
	if err := db.SetCaps("dan", "quarry", "a"); err != nil {
		t.Fatal(err)
	}
	people, err := db.TeamPeople("core")
	if err != nil {
		t.Fatal(err)
	}
	var order []string
	for _, p := range people {
		order = append(order, p.Account)
	}
	// lou owns it, ann is a member, dan only holds a direct grant.
	want := []string{"lou", "ann", "dan"}
	for i := range want {
		if i >= len(order) || order[i] != want[i] {
			t.Fatalf("FAIL: order = %v, want %v", order, want)
		}
	}
}

// A team holding no repositories has members and nobody reaching
// anything, which the screen says rather than listing people against
// nothing.
func TestATeamWithNoReposStillListsItsMembers(t *testing.T) {
	db := teamDB(t)
	if err := db.AddTeam("core", "", "", "dan"); err != nil {
		t.Fatal(err)
	}
	people, err := db.TeamPeople("core")
	if err != nil {
		t.Fatal(err)
	}
	if len(people) != 1 || people[0].Account != "dan" {
		t.Fatalf("FAIL: people = %+v", people)
	}
	if len(people[0].Direct) != 0 {
		t.Errorf("FAIL: direct grants on a team holding nothing: %+v", people[0].Direct)
	}
}

// Somebody who is both a member and holds a string of their own shows
// both routes, because the page has to be able to say which is which.
func TestBothRoutesAppearOnOnePerson(t *testing.T) {
	db := teamDB(t)
	if err := db.AddTeam("core", "", "", "dan"); err != nil {
		t.Fatal(err)
	}
	if err := db.SetTeamRepo("core", "quarry", "dan"); err != nil {
		t.Fatal(err)
	}
	if err := db.SetTeamMember("core", "lou", "i", false, true); err != nil {
		t.Fatal(err)
	}
	if err := db.SetCaps("lou", "quarry", "a"); err != nil {
		t.Fatal(err)
	}
	people, err := db.TeamPeople("core")
	if err != nil {
		t.Fatal(err)
	}
	for _, p := range people {
		if p.Account != "lou" {
			continue
		}
		if !p.Member || p.Caps != "i" {
			t.Errorf("FAIL: the team route is missing: %+v", p)
		}
		if len(p.Direct) != 1 || p.Direct[0].Caps != "a" {
			t.Errorf("FAIL: the direct route is missing: %+v", p.Direct)
		}
		return
	}
	t.Fatal("FAIL: lou is not on the screen at all")
}

// The paged read must be the unpaged read, cut into pages — same
// people, same order, same detail. Anything else and the screen shows
// one population while the count describes another, which is the shape
// of bug this whole area has already produced twice.
func TestTeamPeoplePageAgreesWithTheWholeList(t *testing.T) {
	db := teamDB(t)
	for _, who := range []string{"owen", "mia", "mel", "moe", "dot", "eve", "fay"} {
		if err := db.AddAccount(who, "pw-"+who+"-long-enough", false); err != nil {
			t.Fatal(err)
		}
	}
	if err := db.AddTeam("core", "", "", "dan"); err != nil {
		t.Fatal(err)
	}
	for _, repo := range []string{"quarry", "zit"} {
		if err := db.SetTeamRepo("core", repo, "dan"); err != nil {
			t.Fatal(err)
		}
	}
	// A spread of every route: owners, plain members, a member who also
	// holds a direct string, and non-members who only hold one.
	if err := db.SetTeamMember("core", "owen", "", true, true); err != nil {
		t.Fatal(err)
	}
	for _, who := range []string{"mia", "mel", "moe"} {
		if err := db.SetTeamMember("core", who, "uv3bBC78", false, true); err != nil {
			t.Fatal(err)
		}
	}
	if err := db.SetCaps("mel", "quarry", "a"); err != nil {
		t.Fatal(err)
	}
	for _, who := range []string{"dot", "eve", "fay"} {
		if err := db.SetCaps(who, "zit", "uv3bBC78"); err != nil {
			t.Fatal(err)
		}
	}

	whole, err := db.TeamPeople("core")
	if err != nil {
		t.Fatal(err)
	}
	n, err := db.TeamPeopleCount("core")
	if err != nil {
		t.Fatal(err)
	}
	if n != len(whole) {
		t.Fatalf("FAIL: count says %d, the list holds %d", n, len(whole))
	}

	// Walk it two at a time and rebuild the list from the pages.
	var paged []TeamPerson
	for off := 0; off < n; off += 2 {
		page, err := db.TeamPeoplePage("core", 2, off)
		if err != nil {
			t.Fatal(err)
		}
		if len(page) == 0 {
			t.Fatalf("FAIL: page at offset %d is empty before the end (%d)", off, n)
		}
		paged = append(paged, page...)
	}
	if len(paged) != len(whole) {
		t.Fatalf("FAIL: the pages hold %d people, the whole list %d", len(paged), len(whole))
	}
	for i := range whole {
		w, p := whole[i], paged[i]
		if w.Account != p.Account || w.Member != p.Member ||
			w.Owner != p.Owner || w.Caps != p.Caps {
			t.Errorf("FAIL: row %d: whole=%+v paged=%+v", i, w, p)
			continue
		}
		if len(w.Direct) != len(p.Direct) {
			t.Errorf("FAIL: %s has %d direct grants whole, %d paged",
				w.Account, len(w.Direct), len(p.Direct))
			continue
		}
		for j := range w.Direct {
			if w.Direct[j] != p.Direct[j] {
				t.Errorf("FAIL: %s direct[%d]: whole=%+v paged=%+v",
					w.Account, j, w.Direct[j], p.Direct[j])
			}
		}
	}

	// Past the end is empty, not an error, and a zero limit asks for
	// nothing rather than for everything.
	if page, err := db.TeamPeoplePage("core", 10, n); err != nil || len(page) != 0 {
		t.Errorf("FAIL: past the end: %d rows, err %v", len(page), err)
	}
	if page, err := db.TeamPeoplePage("core", 0, 0); err != nil || len(page) != 0 {
		t.Errorf("FAIL: a zero limit returned %d rows, err %v", len(page), err)
	}
}