package api
import (
"context"
"path/filepath"
"testing"
"craftdesign.group/quarry/internal/oltp"
"craftdesign.group/quarry/internal/ops"
)
// Every row of repo.list and me.repos, and repo.get, says where the
// repository appears: handle, namespace and address, the same as
// repo.add answered when it was made (quarry#422). A repository in a
// person's namespace and one in a team's agree with repo.add; a private
// repository stays out of an anonymous list.
func TestRepositoryIdentityInListsAndGet(t *testing.T) {
db, err := oltp.Open(filepath.Join(t.TempDir(), "o.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { db.Close() })
if err := db.AddAccount("ada", "a-long-enough-password", false); err != nil {
t.Fatal(err)
}
if err := db.AddTeam("crew", "Crew", "", "ada"); err != nil {
t.Fatal(err)
}
shown := map[string][2]string{
"mine": {"ada", "mine"},
"theirs": {"crew", "theirs"},
"secret": {"ada", "secret"},
}
address := func(key string) (string, string, bool) {
a, ok := shown[key]
return a[0], a[1], ok
}
reg := &fakeRegistry{}
repos := NewRepos(map[string]*Repo{
"mine": {Index: wikiIndex(t)}, "theirs": {Index: wikiIndex(t)}, "secret": {Index: wikiIndex(t)},
})
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: repos, Registry: reg, Address: address,
Accounts: stubAccounts{grants: map[string]map[string]string{
"ada": {"mine": "admin", "theirs": "read", "secret": "admin"}}},
Teams: db,
CanRead: func(ctx context.Context, name string) bool { return name != "secret" || ops.User(ctx) == "ada" },
}); err != nil {
t.Fatal(err)
}
as := ops.WithUser(context.Background(), "ada")
// What repo.add answers for each.
mineAdd, err := r.Invoke(as, "repo.add", repoAddIn{Name: "mine", Create: true})
if err != nil {
t.Fatal(err)
}
theirsAdd, err := r.Invoke(as, "repo.add", repoAddIn{Name: "theirs", Namespace: "crew", Create: true})
if err != nil {
t.Fatal(err)
}
want := map[string]repoAddOut{"mine": mineAdd.(repoAddOut), "theirs": theirsAdd.(repoAddOut)}
agree := func(where, key, handle, ns, addr string) {
t.Helper()
w, ok := want[key]
if !ok {
return
}
if handle != w.Handle || ns != w.Namespace || addr != w.Address {
t.Errorf("FAIL: %s: %s is %q/%q/%q, repo.add said %q/%q/%q", where, key, handle, ns, addr, w.Handle, w.Namespace, w.Address)
}
if key != w.Name {
t.Errorf("FAIL: %s: name %q is not repo.add's key %q", where, key, w.Name)
}
}
got, err := r.Invoke(as, "repo.list", repoListIn{})
if err != nil {
t.Fatal(err)
}
seen := map[string]bool{}
for _, row := range got.(repoListOut).Repos {
seen[row.Name] = true
if row.Handle == "" || row.Address == "" {
t.Errorf("FAIL: repo.list row %q lacks handle or address: %+v", row.Name, row)
}
agree("repo.list", row.Name, row.Handle, row.Namespace, row.Address)
}
if !seen["mine"] || !seen["theirs"] || !seen["secret"] {
t.Errorf("FAIL: the owner's list is missing a repository: %v", seen)
}
got, err = r.Invoke(as, "me.repos", meReposIn{})
if err != nil {
t.Fatal(err)
}
if n := len(got.(meReposOut).Repos); n != 3 {
t.Errorf("FAIL: me.repos has %d rows, want 3", n)
}
for _, row := range got.(meReposOut).Repos {
if row.Handle == "" || row.Address == "" {
t.Errorf("FAIL: me.repos row %q lacks handle or address: %+v", row.Name, row)
}
agree("me.repos", row.Name, row.Handle, row.Namespace, row.Address)
}
for key := range want {
info, err := r.Invoke(as, "repo.get", repoGetIn{Repo: key})
if err != nil {
t.Fatal(err)
}
i := info.(repoInfo)
agree("repo.get", key, i.Handle, i.Namespace, i.Address)
}
// No change to who sees what: an anonymous list omits the private one.
got, err = r.Invoke(context.Background(), "repo.list", repoListIn{})
if err != nil {
t.Fatal(err)
}
for _, row := range got.(repoListOut).Repos {
if row.Name == "secret" {
t.Errorf("FAIL: an anonymous list shows a private repository: %+v", row)
}
}
if n := len(got.(repoListOut).Repos); n != 2 {
t.Errorf("FAIL: an anonymous list has %d rows, want 2", n)
}
t.Log("ok")
}
// A key the address book does not know appears by itself, in no
// namespace, which is how repoLabel already reads it.
func TestRepositoryIdentityWithoutAnAddressBook(t *testing.T) {
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"solo": {Index: wikiIndex(t)}})}); err != nil {
t.Fatal(err)
}
got, err := r.Invoke(context.Background(), "repo.get", repoGetIn{Repo: "solo"})
if err != nil {
t.Fatal(err)
}
if i := got.(repoInfo); i.Handle != "solo" || i.Namespace != "" || i.Address != "solo" {
t.Errorf("FAIL: %+v", i)
}
t.Log("ok")
}