Skip to content
quarry Artifact [c4b9aaf5bf]
Login

Artifact [c4b9aaf5bf]

Artifact c4b9aaf5bf9bc47fef5f411ed1929e858544cf7f681a9f13b64047a5df20c9d0:


package harness

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

// Leaf status is repo-state: the repo-state RFC defines it and zit
// derives and caches it. quarry must not re-derive it.
//
// Its own predicate answered "no check-in lists this one as a parent" —
// which §7.2 names explicitly as NOT the canonical test. The canonical
// one is "no child ON THE SAME BRANCH", and open leaves additionally
// exclude an active closed tag. On the fossil corpus that was 490
// marked against a true 1432, and a different set: a forge stating
// false history about a store that is entirely correct.
//
// The fixture branches deliberately. On a linear history the two
// definitions agree and this test would pass while the bug stood.
func TestLeafMarksAgreeWithZit(t *testing.T) {
	repo := buildStore(t, "leafparity")
	if err := os.WriteFile(filepath.Join(repo, "side.txt"), []byte("side\n"), 0o644); err != nil {
		t.Fatal(err)
	}
	zrun(t, repo, "add", "side.txt")
	// --branch starts a new branch AT this check-in, so trunk's previous
	// tip keeps a child that is not on trunk: exactly the shape where
	// "no child at all" and "no same-branch child" disagree.
	zrun(t, repo, "commit", "-m", "work on the side branch", "--branch", "side")

	srv := ingestAndServe(t, map[string]string{"alpha": repo}, t.TempDir())

	// zit's answer, which is the definition.
	out, err := exec.Command("zit", "leaves", "-R", repo).Output()
	if err != nil {
		t.Fatalf("zit leaves: %v", err)
	}
	want := map[string]bool{}
	for _, line := range strings.Split(string(out), "\n") {
		f := strings.Fields(strings.TrimPrefix(strings.TrimSpace(line), "* "))
		if len(f) > 0 && len(f[0]) >= 40 {
			want[f[0]] = true
		}
	}
	if len(want) == 0 {
		t.Fatal("zit reported no leaves for the fixture")
	}

	resp, err := srv.Client().Get(srv.URL + "/api/v1/r/alpha/timeline?limit=200")
	if err != nil {
		t.Fatal(err)
	}
	defer resp.Body.Close()
	body, _ := io.ReadAll(resp.Body)
	var tl struct {
		Entries []struct {
			Kind   string `json:"kind"`
			Hash   string `json:"hash"`
			IsLeaf bool   `json:"isLeaf"`
		} `json:"entries"`
	}
	if err := json.Unmarshal(body, &tl); err != nil {
		t.Fatal(err)
	}

	checkins, marked, disagree := 0, 0, 0
	for _, e := range tl.Entries {
		if e.Kind != "ci" {
			continue
		}
		checkins++
		if e.IsLeaf {
			marked++
		}
		if e.IsLeaf != want[e.Hash] {
			disagree++
			t.Errorf("check-in %s: quarry says leaf=%v, zit says leaf=%v",
				e.Hash[:12], e.IsLeaf, want[e.Hash])
		}
	}
	if checkins == 0 {
		t.Fatal("no check-ins on the timeline")
	}
	if disagree == 0 {
		t.Logf("%d check-ins, %d marked leaf, agreeing with zit's %d", checkins, marked, len(want))
	}
}