Skip to content
quarry Artifact [ce3c86cb79]
Login

Artifact [ce3c86cb79]

Artifact ce3c86cb79ccad053fa2bd7196c4f4fb81f5a0b1e41ff15e56a8ae80524e7311:


package api

import (
	"context"
	"encoding/json"
	"sync"
	"testing"

	"craftdesign.group/quarry/internal/ops"
)

// remarshalT re-decodes an op's output into its typed form.
func remarshalT(t *testing.T, in, out any) {
	t.Helper()
	b, err := json.Marshal(in)
	if err != nil {
		t.Fatal(err)
	}
	if err := json.Unmarshal(b, out); err != nil {
		t.Fatal(err)
	}
}

type fakePraise struct {
	mu  sync.Mutex
	set map[string]map[string]bool // repo -> account -> praised
}

func newFakePraise() *fakePraise {
	return &fakePraise{set: map[string]map[string]bool{}}
}

func (f *fakePraise) SetPraise(repo, account string, on bool) (bool, error) {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.set[repo] == nil {
		f.set[repo] = map[string]bool{}
	}
	if on {
		f.set[repo][account] = true
	} else {
		delete(f.set[repo], account)
	}
	return on, nil
}

func (f *fakePraise) Praised(repo, account string) bool { return f.set[repo][account] }

func (f *fakePraise) PraiseCount(repo string) int { return len(f.set[repo]) }

func (f *fakePraise) PraisedBy(account string) ([]string, error) {
	var out []string
	for _, repo := range []string{"alpha", "beta", "secret"} {
		if f.set[repo][account] {
			out = append(out, repo)
		}
	}
	return out, nil
}

// The rule: any signed-in account may praise any repository it can see.
// No capability letter is involved, so an account with no grant at all
// must succeed.
func TestPraiseNeedsOnlyASignedInAccount(t *testing.T) {
	r := ops.NewRegistry()
	repos := testRepos(wikiIndex(t))
	mustRepo(t, repos, "test").Code = "c1"
	if err := RegisterAll(r, Deps{Version: "v", Repos: repos, Praise: newFakePraise()}); err != nil {
		t.Fatal(err)
	}
	ctx := ops.WithUser(context.Background(), "grantless")
	out, err := r.Invoke(ctx, "praise.set", praiseSetIn{Repo: "test", On: true})
	if err != nil {
		t.Fatalf("a signed-in account with no grant could not praise: %v", err)
	}
	var got praiseSetOut
	remarshalT(t, out, &got)
	if !got.Praised || got.Count != 1 {
		t.Errorf("praised=%v count=%d, want true 1", got.Praised, got.Count)
	}
}

// Praise must be attributable, so anonymous is refused rather than
// pooling every stranger into one shared praise.
func TestAnonymousCannotPraise(t *testing.T) {
	r := ops.NewRegistry()
	repos := testRepos(wikiIndex(t))
	mustRepo(t, repos, "test").Code = "c1"
	if err := RegisterAll(r, Deps{Version: "v", Repos: repos, Praise: newFakePraise()}); err != nil {
		t.Fatal(err)
	}
	if _, err := r.Invoke(context.Background(), "praise.set",
		praiseSetIn{Repo: "test", On: true}); err == nil {
		t.Error("anonymous praise was accepted")
	}
}

// On is a desired state, not a toggle: a retried request must land where
// the reader clicked rather than on the opposite of it.
func TestPraiseIsIdempotent(t *testing.T) {
	r := ops.NewRegistry()
	repos := testRepos(wikiIndex(t))
	mustRepo(t, repos, "test").Code = "c1"
	if err := RegisterAll(r, Deps{Version: "v", Repos: repos, Praise: newFakePraise()}); err != nil {
		t.Fatal(err)
	}
	ctx := ops.WithUser(context.Background(), "dan")
	for i := 0; i < 3; i++ {
		out, err := r.Invoke(ctx, "praise.set", praiseSetIn{Repo: "test", On: true})
		if err != nil {
			t.Fatal(err)
		}
		var got praiseSetOut
		remarshalT(t, out, &got)
		if !got.Praised || got.Count != 1 {
			t.Fatalf("attempt %d: praised=%v count=%d", i, got.Praised, got.Count)
		}
	}
}

// A listing is filtered by the VIEWER. Praise is public; the existence
// of a private repository is not.
func TestPraiseListHidesWhatTheViewerCannotRead(t *testing.T) {
	r := ops.NewRegistry()
	ix := wikiIndex(t)
	repos := NewRepos(map[string]*Repo{
		"alpha": {Index: ix, Code: "c-alpha"},
		"beta":  {Index: ix, Code: "c-beta"},
		// Registered, and deliberately unreadable to the viewer below.
		"secret": {Index: ix, Code: "c-secret"},
	})
	fp := newFakePraise()
	for _, n := range []string{"alpha", "beta", "secret"} {
		fp.SetPraise(n, "dan", true)
	}
	if err := RegisterAll(r, Deps{Version: "v", Repos: repos, Praise: fp,
		CanRead: func(_ context.Context, repo string) bool { return repo != "secret" },
	}); err != nil {
		t.Fatal(err)
	}
	out, err := r.Invoke(context.Background(), "praise.list", praiseListIn{User: "dan"})
	if err != nil {
		t.Fatal(err)
	}
	var got praiseListOut
	remarshalT(t, out, &got)
	for _, repo := range got.Repos {
		if repo == "secret" {
			t.Fatalf("a private repository leaked through a praise listing: %v", got.Repos)
		}
	}
	if len(got.Repos) != 2 {
		t.Errorf("listed %v, want the two readable ones", got.Repos)
	}
}