Skip to content
quarry Artifact [26d41001cf]
Login

Artifact [26d41001cf]

Artifact 26d41001cf591ea310fb0e78f3b9b324571ece89356e8ebcae193c6cb5638b8e:


package api

import (
	"errors"
	"path/filepath"
	"strings"
	"testing"
	"time"

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

// The accept button in a landing's email arms the landing at the tip the
// email was about: arming names that tip, and an arm for a tip the
// branch has moved past is refused, so a push after the email is never
// accepted by a reader who has not seen it.
func TestArmingAtATipRefusesAMovedBranch(t *testing.T) {
	dir, id, ix, pipeSync := acceptStore(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("dan", "a-long-enough-password", false); err != nil {
		t.Fatal(err)
	}
	d := Deps{Version: "v", Auto: db, Repos: NewRepos(map[string]*Repo{
		"test": {Index: ix, Zit: zitcli.New(dir), Sync: pipeSync}})}
	r := ops.NewRegistry()
	if err := RegisterAll(r, d); err != nil {
		t.Fatal(err)
	}
	tip := ztip(t, dir, "feat")
	arm := func(at string) error {
		_, err := r.Invoke(as("dan", "i"), "landing.auto", landingAutoIn{Repo: "test", ID: id, On: true, Tip: at})
		return err
	}
	for _, stale := range []string{"0000000000000000", "ab"} {
		if err := arm(stale); !errors.Is(err, ops.ErrInvalid) {
			t.Errorf("FAIL: arming at tip %q, which the branch is not at: %v", stale, err)
		}
	}
	if rows, _ := db.AutoAccepts(); len(rows) != 0 {
		t.Fatalf("FAIL: a refused arm was stored: %+v", rows)
	}
	if err := arm(tip[:12]); err != nil {
		t.Fatalf("FAIL: arming at the branch's own tip: %v", err)
	}
	rows, _ := db.AutoAccepts()
	if len(rows) != 1 || rows[0].Tip != tip || rows[0].Account != "dan" {
		t.Fatalf("FAIL: the arm stored %+v, want dan at %s", rows, tip)
	}
	t.Log("ok: an arm names the tip it was shown and refuses a branch that moved past it")

	fn := &fakeNotify{}
	d.Notify, d.Owners = fn, oneOwner("alice")
	d.notifyOpened("test", id, "feat", "trunk", "bob")
	evs := waitNotify(fn, 1)
	if len(evs) != 1 || evs[0].Payload["approve"] != "/r/test/landing/"+id+"/approve?tip="+tip {
		t.Fatalf("FAIL: the accepter's notice carries approve %v, want the page at tip %s", evs, tip)
	}
	t.Log("ok: the accepter's notice links the approve page at the branch's tip")
}

// An open landing's email to an accepter carries an Accept button to the
// approve page, naming the branch tip; the text body carries the same
// link, and a settled landing's email has no such button.
func TestALandingEmailCarriesAnAcceptButton(t *testing.T) {
	approve := "https://forge.example/r/alpha/landing/" + landingHash + "/approve?tip=abcdef012345"
	d := Deps{Origin: "https://forge.example"}
	e := d.withMailHTML(NotifyEvent{Workflow: "quarry-landing", To: "alice", Payload: map[string]any{
		"repo": "alpha", "from": "bob", "number": 7, "surface": "landing", "state": "open",
		"title": "Add x", "summary": "bob proposed #7 feature-x → trunk",
		"link": "https://forge.example/r/alpha/landing/" + landingHash, "approve": approve,
	}}, time.Now())
	h, _ := e.Payload["html"].(string)
	if !strings.Contains(h, `href="`+strings.ReplaceAll(approve, "&", "&amp;")+`"`) || !strings.Contains(h, ">Accept</a>") {
		t.Fatalf("FAIL: the open landing's email has no Accept button to the approve page:\n%s", h)
	}
	if !strings.Contains(h, ">Open the landing</a>") {
		t.Error("FAIL: the Accept button replaced the way to open the landing")
	}
	settled := d.withMailHTML(NotifyEvent{Workflow: "quarry-landing", To: "bob", Payload: map[string]any{
		"repo": "alpha", "from": "alice", "number": 7, "surface": "landing", "state": "accepted",
		"summary": "alice accepted #7", "link": "https://forge.example/r/alpha/landing/" + landingHash, "approve": approve,
	}}, time.Now())
	if h, _ := settled.Payload["html"].(string); strings.Contains(h, ">Accept</a>") {
		t.Error("FAIL: a settled landing's email offers to accept it")
	}
	t.Log("ok: an open landing's email offers Accept beside Open the landing")
}