Skip to content
quarry Artifact [8c84e618c1]
Login

Artifact [8c84e618c1]

Artifact 8c84e618c122fccab39e0dca08e19fc241bd2ea73ac53b095685100a4e206aac:


package api

import (
	"errors"
	"os"
	"path/filepath"
	"testing"

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

// Ticket #386: a branch whose landing was settled by a merge pushed from
// a clone, with no accept reply, could not be proposed again: landing.open
// answered 500. The new work opens as a new landing and the old one stays
// accepted.
func TestABranchMergedFromACloneOpensANewLanding(t *testing.T) {
	dir, ids, ix, pipeSync := premergeStore(t)
	zrun(t, dir, "update", "trunk")
	zrun(t, dir, "merge", "one")
	zrun(t, dir, "commit", "-m", "merge one from a clone", "--user", "u")
	zrun(t, dir, "update", "one")
	zwrite(t, dir, "one.txt", "one\nmore\n")
	zrun(t, dir, "commit", "-m", "new work on one", "--user", "u")
	if _, err := pipeSync(); err != nil {
		t.Fatal(err)
	}
	if old, ok, err := ix.Landing(ids[0]); err != nil || !ok || old.State != "accepted" {
		t.Fatalf("setup: the clone-merged landing reads %+v (found %v, %v), want accepted", old, ok, err)
	}

	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v", LandingNumbers: landingNumberDB(t),
		Repos: NewRepos(map[string]*Repo{"test": {Index: ix, Zit: zitcli.New(dir), Sync: pipeSync}})}); err != nil {
		t.Fatal(err)
	}
	out, err := r.Invoke(as("dan", "3"), "landing.open",
		landingOpenIn{Repo: "test", Branch: "one", Target: "trunk", Message: "propose the new work"})
	if err != nil {
		t.Fatalf("FAIL: landing.open after a clone merge: %v", err)
	}
	got := out.(landingOpenOut)
	if got.ID == "" || got.ID == ids[0] {
		t.Fatalf("FAIL: landing.open answered id %q; want a new landing, not %s", got.ID, ids[0])
	}
	if l, ok, err := ix.OpenLandingOn("one"); err != nil || !ok || l.ID != got.ID {
		t.Fatalf("FAIL: open landing on one = %+v (found %v, %v), want %s", l, ok, err, got.ID)
	}
	if old, ok, err := ix.Landing(ids[0]); err != nil || !ok || old.State != "accepted" {
		t.Fatalf("FAIL: the clone-merged landing now reads %+v (found %v, %v), want accepted", old, ok, err)
	}

	// #390: the accepted landing lists what it landed, not the work
	// committed on its branch afterwards, and the new landing lists only
	// that new work, not the check-in the first one already merged.
	comments := func(id string) (tip string, cs []string) {
		t.Helper()
		g, err := r.Invoke(as("dan", "3"), "landing.get", landingGetIn{Repo: "test", ID: id})
		if err != nil {
			t.Fatal(err)
		}
		lg := g.(landingGetOut)
		for _, c := range lg.Commits {
			cs = append(cs, c.Comment)
		}
		return lg.LandedTip, cs
	}
	if tip, cs := comments(ids[0]); tip == "" || len(cs) != 1 || cs[0] != "one" {
		t.Errorf("FAIL: the accepted landing lists %q (landed tip %q), want only \"one\"", cs, tip)
	}
	if _, cs := comments(got.ID); len(cs) != 1 || cs[0] != "new work on one" {
		t.Errorf("FAIL: the new landing lists %q, want only \"new work on one\"", cs)
	}
}

// A refusal from the store is the proposal's fault, said in the store's
// words: a 400, never a 500.
func TestAStoreRefusalToOpenALandingIsInvalidNotInternal(t *testing.T) {
	dir, _, ix, pipeSync := premergeStore(t)
	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v",
		Repos: NewRepos(map[string]*Repo{"test": {Index: ix, Zit: zitcli.New(dir), Sync: pipeSync}})}); err != nil {
		t.Fatal(err)
	}
	fakeZitScript(t, `echo "zit landing: 'one' already has an open landing (0123456789ab)" >&2; exit 1`)
	_, err := r.Invoke(as("dan", "3"), "landing.open",
		landingOpenIn{Repo: "test", Branch: "three", Target: "trunk", Message: "m"})
	if !errors.Is(err, ops.ErrInvalid) {
		t.Fatalf("FAIL: a store refusal answered %v; want ops.ErrInvalid", err)
	}
}

// fakeZitScript puts a zit on PATH that runs script and nothing else.
func fakeZitScript(t *testing.T, script string) {
	t.Helper()
	bin := t.TempDir()
	if err := os.WriteFile(filepath.Join(bin, "zit"), []byte("#!/bin/sh\n"+script+"\n"), 0o755); err != nil {
		t.Fatal(err)
	}
	t.Setenv("PATH", bin+string(os.PathListSeparator)+os.Getenv("PATH"))
}