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, "&", "&")+`"`) || !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")
}
// A sign-off's email (RFC 0027) links its own page and says what is
// asked; a forge-wide request names no repository and still reads.
func TestASignOffEmailLinksItsPage(t *testing.T) {
page := "https://forge.example/me/signoffs/35d8a17bda1c0a52d6ab0a60"
d := Deps{Origin: "https://forge.example"}
e := d.withMailHTML(NotifyEvent{Workflow: "quarry-signoff", To: "dmestas", Payload: map[string]any{
"repo": "", "from": "zeitgeist", "surface": "signoff",
"summary": `agent.new for rajeev ("a QA tester")`, "link": page,
}}, time.Now())
h, _ := e.Payload["html"].(string)
if !strings.Contains(h, `href="`+page+`"`) {
t.Fatalf("FAIL: the sign-off email does not link its page:\n%s", h)
}
if !strings.Contains(h, "agent.new for rajeev") {
t.Errorf("FAIL: the sign-off email does not say what is asked:\n%s", h)
}
if strings.Contains(h, "act on ,") {
t.Errorf("FAIL: a forge-wide request's email names an empty repository:\n%s", h)
}
}