Skip to content
quarry Artifact [8b0a5b2bce]
Login

Artifact [8b0a5b2bce]

Artifact 8b0a5b2bce835a93a2a11eb7a76a7c3f5d715b2ab009d23eed3d1421ed1a30ef:


package harness

import (
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

// A conflicted landing must render as BLOCKED, naming the files, with accept
// disabled — never offered-and-failing. zit#131 gives the forge the only safe
// way to know: ask the store (`landing show`), because on a server the
// attempt IS the damage.
//
// This drives a real store with genuinely divergent branches rather than a
// fixture, because the thing under test is the reading of zit's answer.
func TestConflictedLandingRendersAsBlocked(t *testing.T) {
	if _, err := exec.LookPath("zit"); err != nil {
		t.Skip("zit not on PATH")
	}
	store := t.TempDir()
	run := func(args ...string) string {
		t.Helper()
		cmd := exec.Command("zit", args...)
		cmd.Dir = store
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Fatalf("zit %v: %v\n%s", args, err, out)
		}
		return strings.TrimSpace(string(out))
	}
	write := func(body string) {
		if err := os.WriteFile(filepath.Join(store, "f.txt"), []byte(body), 0o644); err != nil {
			t.Fatal(err)
		}
	}
	run("init")
	write("alpha\nbeta\ngamma\n")
	run("add", "f.txt")
	run("commit", "-m", "base")
	base := run("status")
	base = firstHash(base)
	// Two edits to the same line, on branches neither of which descends
	// from the other: the shape a content merge cannot resolve.
	write("ALPHA-from-feat\nbeta\ngamma\n")
	run("commit", "-m", "feat edit", "--branch", "feat")
	run("update", base)
	write("ALPHA-from-trunk\nbeta\ngamma\n")
	run("commit", "-m", "trunk moves")
	run("landing", "open", "--branch", "feat", "-m", "land feat", "--user", "dan")

	srv := ingestAndServe(t, map[string]string{"alpha": store}, t.TempDir())
	resp, err := srv.Client().Get(srv.URL + "/r/alpha/landing")
	if err != nil {
		t.Fatal(err)
	}
	b, _ := io.ReadAll(resp.Body)
	resp.Body.Close()
	id := betweenLast(string(b), `/r/alpha/landing/`, `"`)
	if id == "" {
		t.Fatalf("the landing never reached the listing:\n%s", b)
	}

	page, err := srv.Client().Get(srv.URL + "/r/alpha/landing/" + id)
	if err != nil {
		t.Fatal(err)
	}
	defer page.Body.Close()
	pb, _ := io.ReadAll(page.Body)
	html := string(pb)

	if !strings.Contains(html, "conflicts with") {
		t.Errorf("a conflicted landing does not say so:\n%s", html)
	}
	// Naming the file is the difference between a warning and an
	// instruction: the reader has to know what to go and fix.
	if !strings.Contains(html, "f.txt") {
		t.Error("the conflicting path is not named")
	}
	// Offering a control that cannot work is the failure this whole
	// mechanism exists to prevent.
	if strings.Contains(html, `formaction="/r/alpha/landing/`+id+`/accept"`) {
		t.Error("accept was offered on a conflicted landing")
	}
}

func firstHash(status string) string {
	for _, f := range strings.Fields(strings.ReplaceAll(status, ")", " ")) {
		if len(f) >= 40 && strings.Trim(f, "0123456789abcdef") == "" {
			return f
		}
	}
	return ""
}

func betweenLast(s, open, close string) string {
	i := strings.Index(s, open)
	if i < 0 {
		return ""
	}
	rest := s[i+len(open):]
	j := strings.Index(rest, close)
	if j < 0 {
		return ""
	}
	return rest[:j]
}