Skip to content
quarry Artifact [94c9bb9295]
Login

Artifact [94c9bb9295]

Artifact 94c9bb92953db2e3b2f92927c7894e65d5829c446a59a7cef1a008de9654ed34:


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)
	}
}

// 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"))
}