Skip to content
quarry Artifact [1f457d41a8]
Login

Artifact [1f457d41a8]

Artifact 1f457d41a8ade5cee56ab7ba48fc3376e4b6e8dc3a45b9370e196fabad2e3ac5:


package api

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"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 []RepoEdit
	actors  []string
	addErr  error
	// owner, when set, answers Owner; otherwise the author owns.
	owner     func(string) (string, error)
	deleted   []string
	removeErr error
	imports   []RepoImport
}

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) Owner(author string) (string, error) {
	if f.owner != nil {
		return f.owner(author)
	}
	return author, 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 {
	f.deleted = append(f.deleted, name)
	return nil
}
func (f *fakeRegistry) Update(name string, edit RepoEdit, by, via string) error {
	f.updates = append(f.updates, edit)
	f.actors = append(f.actors, by)
	return nil
}
func (f *fakeRegistry) Imports() ([]RepoImport, error) { return f.imports, 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 whole (the registrar is asked to Delete
// what it just Added: the store, the mirror and every row with it, not
// merely to stop serving it) and the caller is told why.
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.deleted) != 1 || reg.deleted[0] != "widgets" {
		t.Fatalf("rollback did not delete the repository: deleted %+v, removed %+v", reg.deleted, reg.removed)
	}
	if !strings.Contains(err.Error(), "boom") {
		t.Errorf("the refusal is hidden: %v", err)
	}
}

// An agent never owns a repository: the person answerable for it does,
// and grants the agent admin so it can do everything its owner can
// (RFC 0021, amended 2026-10-21).
func TestAnAgentsRepositoryIsOwnedByItsAnswerablePerson(t *testing.T) {
	reg := &fakeRegistry{owner: func(a string) (string, error) {
		if a == "bot" {
			return "alice", nil
		}
		return a, nil
	}}
	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(), "bot")
	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.Errorf("FAIL: the answerable person's grant = %q, want admin", got)
	}
	if got := accts.grants["bot"]["widgets"]; got != "admin" {
		t.Errorf("FAIL: the agent's grant = %q, want admin", got)
	}
}

// An agent nobody answers for is refused before anything is made.
func TestAnAgentNobodyAnswersForCreatesNothing(t *testing.T) {
	reg := &fakeRegistry{owner: func(string) (string, error) {
		return "", fmt.Errorf("bot answers to no admin: %w", ops.ErrForbidden)
	}}
	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(nil), Registry: reg, Accounts: newFakeAccounts()}); err != nil {
		t.Fatal(err)
	}
	ctx := ops.WithUser(context.Background(), "bot")
	_, err := r.Invoke(ctx, "repo.add", repoAddIn{Name: "widgets", Clone: "https://example.org/r"})
	if !errors.Is(err, ops.ErrForbidden) || len(reg.added) != 0 || len(reg.deleted) != 0 {
		t.Errorf("FAIL: err %v, added %v, deleted %v", err, reg.added, reg.deleted)
	}
}

// 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)
	}
	// The response reports what the repository HOLDS, read back after
	// the write rather than echoed from the request. With no store
	// behind this fake that is the default (on), which is what the
	// write asked for either way.
	res := out.(repoUpdateOut)
	if !res.Mirrorable {
		t.Errorf("repo.update reported mirrorable off after being asked to turn it on: %+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")
	}
}

// zitOnly is a Git import's format and nothing else's: asked of any
// other repository it is refused, and on a Git clone it reaches the
// registry.
func TestZitOnlyIsAGitImportsFormat(t *testing.T) {
	reg := &fakeRegistry{}
	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(nil), Registry: reg}); err != nil {
		t.Fatal(err)
	}
	ctx := ops.WithUser(context.Background(), "alice")
	if _, err := r.Invoke(ctx, "repo.add", repoAddIn{Name: "a", Clone: "https://fossil-scm.org/home", ZitOnly: true}); !errors.Is(err, ops.ErrInvalid) {
		t.Errorf("FAIL: zitOnly on a Fossil clone: %v", err)
	}
	if _, err := r.Invoke(ctx, "repo.add", repoAddIn{Name: "b", Clone: "https://github.com/a/b", Git: true, ZitOnly: true}); err != nil {
		t.Fatal(err)
	}
	if len(reg.added) != 1 || !reg.added[0].ZitOnly || !reg.added[0].Git {
		t.Errorf("FAIL: the registry was handed %+v", reg.added)
	}
}

// repo.import tells somebody with a role on a repository how its copy
// is going, and reads as a repository that does not exist to anybody
// else.
func TestRepoImportAnswersOnlyThoseWithARole(t *testing.T) {
	reg := &fakeRegistry{imports: []RepoImport{{Name: "copying", State: "cloning", Bytes: 42}, {Name: "broke", State: "failed", Error: "no such repository"}}}
	accts := newFakeAccounts()
	accts.grants["alice"] = map[string]string{"copying": "admin", "broke": "admin", "done": "admin"}
	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"done": {}}), Registry: reg, Accounts: accts}); err != nil {
		t.Fatal(err)
	}
	alice := ops.WithUser(context.Background(), "alice")
	for name, want := range map[string]string{"copying": "copying", "broke": "failed", "done": "ready"} {
		out, err := r.Invoke(alice, "repo.import", repoImportIn{Name: name})
		if err != nil || out.(repoImportOut).State != want {
			t.Errorf("FAIL: %s: %+v %v, want %s", name, out, err, want)
		}
	}
	if _, err := r.Invoke(ops.WithUser(context.Background(), "mallory"), "repo.import", repoImportIn{Name: "copying"}); !errors.Is(err, ops.ErrNotFound) {
		t.Errorf("FAIL: somebody with no role saw the copy: %v", err)
	}
}