Skip to content
quarry Artifact [7123a29c89]
Login

Artifact [7123a29c89]

Artifact 7123a29c896e2d18cb42299c3599bc78ff98fb38f9f26484d8b19ff90a4b529e:


package api

import (
	"archive/tar"
	"compress/gzip"
	"errors"
	"io"
	"os/exec"
	"strings"
	"testing"

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

// A PAIR'S ARCHIVE IS THE MERGED TREE, AND A CONFLICT IS ITS ANSWER.
// The runner fetches /archive/<subject> for every run; with a pair as
// the subject it gets the tree an accept would write, or a refusal
// naming the files that do not merge, which fails the step with them.
func TestAPairArchiveIsTheMergeOrItsConflict(t *testing.T) {
	if _, err := exec.LookPath("zit"); err != nil {
		t.Skip("zit not on PATH")
	}
	dir := t.TempDir()
	zrun(t, dir, "init", ".")
	zwrite(t, dir, "f.txt", "top\nMID\nbottom\n")
	zwrite(t, dir, "g.txt", "base\n")
	zrun(t, dir, "add", "f.txt", "g.txt")
	zrun(t, dir, "commit", "-m", "root", "--user", "u")
	zwrite(t, dir, "f.txt", "top\nMID\nBOTTOM\n")
	zrun(t, dir, "commit", "-m", "ok", "--branch", "ok", "--user", "u")
	zrun(t, dir, "update", "trunk")
	zwrite(t, dir, "g.txt", "THEIRS\n")
	zrun(t, dir, "commit", "-m", "bad", "--branch", "bad", "--user", "u")
	zrun(t, dir, "update", "trunk")
	zwrite(t, dir, "f.txt", "TOP\nMID\nbottom\n")
	zwrite(t, dir, "g.txt", "OURS\n")
	zrun(t, dir, "commit", "-m", "t", "--user", "u")
	trunk, ok, bad := ztip(t, dir, "trunk"), ztip(t, dir, "ok"), ztip(t, dir, "bad")

	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(map[string]*Repo{
		"test": {Zit: zitcli.New(dir), Index: nil}})}); err != nil {
		t.Fatal(err)
	}
	get := func(version string) (ops.File, error) {
		t.Helper()
		out, err := r.Invoke(as("dan", "z"), "archive.get", archiveIn{Repo: "test", Version: version})
		if err != nil {
			return nil, err
		}
		f := out.(ops.File)
		rc, _, err := f.Open()
		if err != nil {
			return nil, err
		}
		rc.Close()
		return f, nil
	}

	f, err := get("pair:" + ok + ":" + trunk)
	if err != nil {
		t.Fatalf("FAIL: a clean pair's archive: %v", err)
	}
	rc, _, _ := f.Open()
	defer rc.Close()
	gz, err := gzip.NewReader(rc)
	if err != nil {
		t.Fatal(err)
	}
	files := map[string]string{}
	tr := tar.NewReader(gz)
	for {
		h, err := tr.Next()
		if errors.Is(err, io.EOF) {
			break
		}
		if err != nil {
			t.Fatal(err)
		}
		_, rel, _ := strings.Cut(h.Name, "/")
		b, _ := io.ReadAll(tr)
		files[rel] = string(b)
	}
	if files["f.txt"] != "TOP\nMID\nBOTTOM\n" {
		t.Errorf("FAIL: f.txt = %q, want the three-way merge", files["f.txt"])
	}

	_, err = get("pair:" + bad + ":" + trunk)
	var ref *ops.Refusal
	if !errors.As(err, &ref) || ref.Code != RefusalMergeConflict || !strings.Contains(ref.Message, "g.txt") {
		t.Fatalf("FAIL: a conflicting pair answered %v, want a %s refusal naming g.txt", err, RefusalMergeConflict)
	}

	if _, err := get("pair:trunk:" + trunk); !errors.Is(err, ops.ErrInvalid) {
		t.Fatalf("FAIL: a pair naming a branch rather than a hash answered %v, want invalid", err)
	}
	t.Log("ok: a clean pair serves the merged tree, a conflict refuses naming its file, a moving name is refused")
}