Skip to content
quarry Artifact [d232ad3e06]
Login

Artifact [d232ad3e06]

Artifact d232ad3e0625264e255acd09b572ea65256c55d649829d17ba40a31277018f7b:


package ingest

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

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

func run(t *testing.T, dir string, args ...string) {
	t.Helper()
	cmd := exec.Command("zit", args...)
	cmd.Dir = dir
	if out, err := cmd.CombinedOutput(); err != nil {
		t.Fatalf("zit %v: %v\n%s", args, err, out)
	}
}

func commitFile(t *testing.T, repo, name, body, msg string) {
	t.Helper()
	if err := os.WriteFile(filepath.Join(repo, name), []byte(body), 0o644); err != nil {
		t.Fatal(err)
	}
	run(t, repo, "add", name)
	run(t, repo, "commit", "-m", msg)
}

func testSetup(t *testing.T) (*zitcli.Client, *index.Index, string) {
	t.Helper()
	if _, err := exec.LookPath("zit"); err != nil {
		t.Skip("zit not on PATH")
	}
	repo := t.TempDir()
	run(t, repo, "init")
	commitFile(t, repo, "a.txt", "alpha\n", "add a")
	commitFile(t, repo, "b.txt", "beta\n", "add b")

	ix, err := index.Open(filepath.Join(t.TempDir(), "index.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { ix.Close() })
	return zitcli.New(repo), ix, repo
}

func TestSyncBootstrapAndIncremental(t *testing.T) {
	cli, ix, repo := testSetup(t)

	n, err := Sync(cli, ix)
	if err != nil {
		t.Fatal(err)
	}
	if n == 0 {
		t.Fatal("bootstrap folded nothing")
	}
	tl, err := ix.Timeline(10)
	if err != nil {
		t.Fatal(err)
	}
	// zit init creates no initial check-in; two commits, two entries.
	// Same-second commits tie on date, so assert membership, not wall
	// order — the ordering guarantee under test is determinism (date
	// then hash), which the disposability harness covers.
	if len(tl) != 2 {
		t.Fatalf("timeline has %d entries: %+v", len(tl), tl)
	}
	comments := map[string]bool{tl[0].Comment: true, tl[1].Comment: true}
	if !comments["add a"] || !comments["add b"] {
		t.Fatalf("timeline contents: %+v", tl)
	}

	// Current cursor: no-op.
	if n, err = Sync(cli, ix); err != nil || n != 0 {
		t.Fatalf("idle sync: n=%d err=%v", n, err)
	}

	// Append converges.
	commitFile(t, repo, "c.txt", "gamma\n", "add c")
	if n, err = Sync(cli, ix); err != nil || n == 0 {
		t.Fatalf("incremental sync: n=%d err=%v", n, err)
	}
	tl, _ = ix.Timeline(10)
	if len(tl) != 3 {
		t.Fatalf("timeline after append: %+v", tl)
	}
}

func TestSyncRecoversFromStoreRewrite(t *testing.T) {
	cli, ix, repo := testSetup(t)
	if _, err := Sync(cli, ix); err != nil {
		t.Fatal(err)
	}
	before, _ := ix.Timeline(10)

	// Rewrite the store under the cursor: shun a blob and reclaim.
	head, err := exec.Command("zit", "ls", "-R", repo, "tip").Output()
	if err != nil {
		t.Fatal(err)
	}
	blob := strings.Fields(string(head))[0]
	run(t, repo, "shun", blob, "--force")

	if _, err := Sync(cli, ix); err != nil {
		t.Fatalf("sync after rewrite should reset and rebuild, got %v", err)
	}
	after, _ := ix.Timeline(10)
	if len(after) != len(before) {
		t.Fatalf("rebuild diverged: %d vs %d check-ins", len(after), len(before))
	}
}