Skip to content
quarry Artifact [fc4536f1e1]
Login

Artifact [fc4536f1e1]

Artifact fc4536f1e153910d39c338420c2d811844d7cb7268fff2fb06d1421036c965d1:


package api

import (
	"context"
	"path/filepath"
	"strings"
	"testing"

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

// An owner watching /t/{name}/settings wakes on the team noun. An offer
// arriving is the strongest reason to wake them — it is a decision
// waiting on them, made by somebody else, on a page they already have
// open — and it raised nothing at all: the offer path returned before
// any raise, so the only thing that reached the stream was its own
// heartbeat.
//
// Withdrawing has the same shape from the other side: the row is gone
// and the owner is looking at a control for a decision that is no
// longer theirs to make.
func TestAnOfferAndItsWithdrawalRaiseTheTeamNoun(t *testing.T) {
	db, err := oltp.Open(filepath.Join(t.TempDir(), "o.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { db.Close() })
	for _, who := range []string{"lou", "ann"} {
		if err := db.AddAccount(who, "a-long-enough-password", false); err != nil {
			t.Fatal(err)
		}
	}
	ev := &fakeEvents{}
	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v",
		Repos:    testRepos(wikiIndex(t)),
		Teams:    db,
		Accounts: acctAdapter{db},
		Events:   ev,
		CapsFor:  func(a, repo string) CapSet { return db.EffectiveCaps(a, repo) },
	}); err != nil {
		t.Fatal(err)
	}
	lou := ops.WithUser(context.Background(), "lou")
	ann := ops.WithUser(context.Background(), "ann")
	if _, err := r.Invoke(lou, "team.create", teamCreateIn{Name: "core"}); err != nil {
		t.Fatal(err)
	}
	if err := db.SetCaps("ann", "test", "a"); err != nil {
		t.Fatal(err)
	}

	// ann administers the repository but does not own the team, so this
	// is an offer rather than a move.
	ev.reset()
	if _, err := r.Invoke(ann, "team.repo.add",
		teamRepoIn{Name: "core", Repo: "test"}); err != nil {
		t.Fatal(err)
	}
	if !ev.sawNoun("core", "team") {
		t.Errorf("FAIL: an offer raised nothing; an owner's open page never wakes. saw %v", ev.subjects())
	} else {
		t.Log("ok: the offer wakes the team")
	}

	ev.reset()
	if _, err := r.Invoke(ann, "team.repo.offer.decline",
		teamRepoIn{Name: "core", Repo: "test"}); err != nil {
		t.Fatal(err)
	}
	if !ev.sawNoun("core", "team") {
		t.Errorf("FAIL: a withdrawal raised nothing. saw %v", ev.subjects())
	} else {
		t.Log("ok: the withdrawal wakes the team")
	}
}

func (f *fakeEvents) reset() {
	f.mu.Lock()
	defer f.mu.Unlock()
	f.seen = nil
}

// sawNoun asks the question the stream asks: the team's name sits in
// the repository slot of the subject, and WatchNoun matches on the
// noun. Not the whole subject, because which team.* action it was is
// the payload's business and not this guard's.
func (f *fakeEvents) sawNoun(team, noun string) bool {
	for _, s := range f.subjects() {
		if strings.Contains(s, "."+team+".") && strings.Contains(s, "."+noun+".") {
			return true
		}
	}
	return false
}