package api
import (
"context"
"errors"
"testing"
"craftdesign.group/quarry/internal/ops"
)
// fakeRegistry is the minimal RepoRegistry a repo.add/repo.update test
// needs: it records what it was asked to do rather than touching a
// filesystem or a real store.
type fakeRegistry struct {
added []RepoSpec
removed []string
updates []repoUpdateIn
addErr error
removeErr error
}
func (f *fakeRegistry) Add(spec RepoSpec) (string, error) {
if f.addErr != nil {
return "", f.addErr
}
f.added = append(f.added, spec)
return "/repos/" + spec.Name, nil
}
func (f *fakeRegistry) Remove(name string) error {
f.removed = append(f.removed, name)
return f.removeErr
}
func (f *fakeRegistry) Delete(name, confirm string) error { return nil }
func (f *fakeRegistry) Update(name, description, visibility, homepage, topics string, mirrorable bool) error {
priv := visibility == "private"
f.updates = append(f.updates, repoUpdateIn{Name: name, Description: &description,
Private: &priv, Homepage: &homepage, Topics: &topics, Mirrorable: &mirrorable})
return nil
}
func (f *fakeRegistry) SetChat(name string, on bool) error { return nil }
func (f *fakeRegistry) Imports() ([]RepoImport, error) { return nil, nil }
// repo.add is open to any signed-in account now — Token, not Admin —
// and carries no site-admin capability requirement.
func TestRepoAddSurfaceIsOpenToAnySignedInAccount(t *testing.T) {
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(nil)}); err != nil {
t.Fatal(err)
}
op, ok := r.Lookup("repo.add")
if !ok {
t.Fatal("repo.add is not registered")
}
if op.Auth != ops.Token {
t.Errorf("repo.add Auth = %v, want ops.Token (any signed-in account)", op.Auth)
}
if op.Caps != "" {
t.Errorf("repo.add Caps = %q, want empty — no site-admin capability requirement", op.Caps)
}
}
// Anyone who is admin of a mirror on THIS forge (which its creator is,
// by construction) may configure it without being a site admin —
// mirror.writes included: it is that repository's own fork to
// reconcile, not the forge operator's call (docs/hosting.md).
func TestMirrorOpsAreOpenIncludingWrites(t *testing.T) {
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(nil)}); err != nil {
t.Fatal(err)
}
for _, name := range []string{"mirror.list", "mirror.set", "mirror.detach", "mirror.run", "mirror.writes"} {
op, ok := r.Lookup(name)
if !ok {
t.Fatalf("%s is not registered", name)
}
if op.Auth != ops.Token {
t.Errorf("%s Auth = %v, want ops.Token", name, op.Auth)
}
if op.Caps != "a" {
t.Errorf("%s Caps = %q, want %q (admin of the mirror, not of the site)", name, op.Caps, "a")
}
}
}
// Creating a repository is the act of owning it: the creator is granted
// admin on it in the same operation, not merely served it.
func TestRepoAddGrantsTheCreatorAdmin(t *testing.T) {
reg := &fakeRegistry{}
accts := newFakeAccounts()
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(nil), Registry: reg, Accounts: accts}); err != nil {
t.Fatal(err)
}
ctx := ops.WithUser(context.Background(), "alice")
if _, err := r.Invoke(ctx, "repo.add", repoAddIn{Name: "widgets", Create: true}); err != nil {
t.Fatal(err)
}
if got := accts.grants["alice"]["widgets"]; got != "admin" {
t.Fatalf("creator's grant = %q, want %q", got, "admin")
}
}
// A repository must not be left ownerless: if the owner grant fails,
// the creation is rolled back (the registrar is asked to Remove what it
// just Added) and the caller is told, rather than serving a repository
// its own creator cannot administer.
func TestRepoAddRollsBackWhenTheOwnerGrantFails(t *testing.T) {
reg := &fakeRegistry{}
accts := newFakeAccounts()
accts.grantErr = errors.New("boom")
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(nil), Registry: reg, Accounts: accts}); err != nil {
t.Fatal(err)
}
ctx := ops.WithUser(context.Background(), "alice")
_, err := r.Invoke(ctx, "repo.add", repoAddIn{Name: "widgets", Create: true})
if err == nil {
t.Fatal("expected an error when the owner grant fails")
}
if len(reg.removed) != 1 || reg.removed[0] != "widgets" {
t.Fatalf("rollback did not remove the repository: %+v", reg.removed)
}
}
// repo.update applies mirrorable whole, like every other field here.
func TestRepoUpdateCarriesMirrorable(t *testing.T) {
reg := &fakeRegistry{}
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"widgets": {}}), Registry: reg}); err != nil {
t.Fatal(err)
}
yes := true
out, err := r.Invoke(context.Background(), "repo.update",
repoUpdateIn{Name: "widgets", Mirrorable: &yes})
if err != nil {
t.Fatal(err)
}
if len(reg.updates) != 1 || reg.updates[0].Mirrorable == nil || !*reg.updates[0].Mirrorable {
t.Fatalf("Update was not asked to store mirrorable=true: %+v", reg.updates)
}
res := out.(repoUpdateOut)
if !res.Mirrorable {
t.Errorf("repo.update did not echo mirrorable back: %+v", res)
}
}
// A private repository reports itself as not mirrorable through
// repo.get even when its own stored flag is on — Deps.Mirrorable is the
// EFFECTIVE answer, private trumps the flag, and a reader should not
// have to separately check Private to know whether mirroring works.
func TestRepoGetReportsPrivateRepositoriesAsNotMirrorable(t *testing.T) {
r := ops.NewRegistry()
d := Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"secret": {}}),
Private: func() map[string]bool { return map[string]bool{"secret": true} },
Mirrorable: func(repo string) bool { return false }, // the effective answer, flag notwithstanding
}
if err := RegisterAll(r, d); err != nil {
t.Fatal(err)
}
out, err := r.Invoke(context.Background(), "repo.get", repoGetIn{Repo: "secret"})
if err != nil {
t.Fatal(err)
}
info := out.(repoInfo)
if !info.Private {
t.Fatal("test setup: repository did not report private")
}
if info.Mirrorable {
t.Error("a private repository reported mirrorable=true")
}
}