Skip to content
quarry Artifact [5bc6680f83]
Login

Artifact [5bc6680f83]

Artifact 5bc6680f831f87646486e8816ab2f133879320b12ebe289eed5f030db43786d3:


package api

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

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

// A Git default follows the source ref, not the newest event still carrying
// trunk. Explicit references remain historical views, even if the import map
// cannot be read. The released query owns that distinction (zit#252).
func TestGitMirrorDefaultFollowsImportedHead(t *testing.T) {
	for _, date := range []string{"2001-01-02T00:00:00Z", "2001-01-03T00:00:00Z"} {
		t.Run(date, func(t *testing.T) {
			ctx := context.Background()
			root := t.TempDir()
			source, store := filepath.Join(root, "source"), filepath.Join(root, "mirror")
			if err := os.Mkdir(source, 0o700); err != nil {
				t.Fatal(err)
			}
			git := func(date string, args ...string) {
				t.Helper()
				cmd := exec.Command("git", append([]string{"-C", source, "-c", "user.name=Test", "-c", "[email protected]"}, args...)...)
				cmd.Env = append(os.Environ(), "GIT_AUTHOR_DATE="+date, "GIT_COMMITTER_DATE="+date)
				if out, err := cmd.CombinedOutput(); err != nil {
					t.Fatalf("git %v: %v: %s", args, err, out)
				}
			}
			commit := func(content, date string) {
				t.Helper()
				if err := os.WriteFile(filepath.Join(source, "README.md"), []byte(content), 0o600); err != nil {
					t.Fatal(err)
				}
				git(date, "add", "README.md")
				git(date, "commit", "-qm", content)
			}
			git(date, "init", "-q", "-b", "main")
			commit("base", "2001-01-01T00:00:00Z")
			commit("old-tip", "2001-01-03T00:00:00Z")
			if _, err := zitcli.ImportGit(ctx, source, store, nil); err != nil {
				t.Fatal(err)
			}
			cli := zitcli.New(store)
			_, oldHash, err := cli.ImportedHead(ctx)
			if err != nil {
				t.Fatal(err)
			}
			git(date, "reset", "-q", "--hard", "HEAD~1")
			commit("rewritten", date)
			if _, err := cli.ImportGitInto(lease.Unguarded("test"), ctx, source, nil); err != nil {
				t.Fatal(err)
			}
			name, head, err := cli.ImportedHead(ctx)
			if err != nil || name != "heads/main" || head == oldHash {
				t.Fatalf("FAIL: imported head %s %s: %v", name, head, err)
			}
			mirrors := memMirrors{"git": {Repo: "git", Git: true, Enabled: true, URL: "https://example.org/source.git"}}
			r := ops.NewRegistry()
			d := Deps{Version: "test", Repos: NewRepos(map[string]*Repo{"git": {Zit: cli}}), Mirrors: mirrors}
			if err := RegisterAll(r, d); err != nil {
				t.Fatal(err)
			}
			got, err := r.Invoke(ctx, "tree.list", treeIn{Repo: "git"})
			if err != nil {
				t.Fatal(err)
			}
			tree := got.(treeOut)
			if tree.Ref != name || tree.Version != head || !strings.Contains(tree.Readme, "rewritten") {
				t.Fatalf("FAIL: default tree did not use imported head: %+v", tree)
			}
			got, err = r.Invoke(ctx, "file.get", fileGetIn{Repo: "git", Hash: tree.Version, Path: "README.md"})
			if err != nil || got.(fileGetOut).Content != "rewritten" {
				t.Fatalf("FAIL: default tree's file link: %v %v", got, err)
			}
			got, err = r.Invoke(ctx, "repo.about", aboutIn{Repo: "git"})
			if err != nil || !strings.Contains(got.(aboutOut).DocHTML, "rewritten") {
				t.Fatalf("FAIL: About did not follow imported head: %v %v", got, err)
			}
			got, err = r.Invoke(ctx, "tree.find", treeFindIn{Repo: "git", Q: "README", Limit: 8})
			if err != nil || got.(treeFindOut).Version != head {
				t.Fatalf("FAIL: default file finder did not follow imported head: %v %v", got, err)
			}
			// Pausing the scheduler does not turn the default back into a
			// date-based branch lookup. Only detaching does that.
			paused := mirrors["git"]
			paused.Enabled = false
			mirrors["git"] = paused
			got, err = r.Invoke(ctx, "tree.list", treeIn{Repo: "git"})
			if err != nil || got.(treeOut).Version != head {
				t.Fatalf("FAIL: paused mirror lost its imported default: %v %v", got, err)
			}
			for _, ref := range []string{oldHash, "trunk"} {
				got, err = r.Invoke(ctx, "tree.list", treeIn{Repo: "git", Version: ref})
				if err != nil {
					t.Fatal(err)
				}
				if ref == oldHash && !strings.Contains(got.(treeOut).Readme, "old-tip") {
					t.Fatal("FAIL: explicit historical view changed")
				}
				if got.(treeOut).Ref != ref {
					t.Fatalf("FAIL: explicit reference %s was replaced", ref)
				}
			}
			// A missing or corrupt map is a failure, never an excuse to use
			// trunk. Historical hash reads do not depend on that metadata.
			mapPath := filepath.Join(store, ".zit-import-map")
			if err := os.WriteFile(mapPath, []byte("broken"), 0o600); err != nil {
				t.Fatal(err)
			}
			for _, op := range []struct {
				name string
				in   any
			}{{"tree.list", treeIn{Repo: "git"}}, {"tree.find", treeFindIn{Repo: "git", Q: "README", Limit: 8}}, {"repo.about", aboutIn{Repo: "git"}}} {
				if _, err := r.Invoke(ctx, op.name, op.in); !errors.Is(err, ops.ErrUnavailable) {
					t.Fatalf("FAIL: %s with a damaged map: %v", op.name, err)
				}
			}
			if _, err := r.Invoke(ctx, "tree.list", treeIn{Repo: "git", Version: oldHash}); err != nil {
				t.Fatalf("FAIL: explicit history requires import metadata: %v", err)
			}
			if err := os.Remove(mapPath); err != nil {
				t.Fatal(err)
			}
			if _, err := r.Invoke(ctx, "tree.list", treeIn{Repo: "git"}); !errors.Is(err, ops.ErrUnavailable) {
				t.Fatalf("FAIL: missing map fell back: %v", err)
			}
			m := mirrors["git"]
			m.Enabled, m.DetachedFrom = false, m.URL
			mirrors["git"] = m
			if _, err := r.Invoke(ctx, "tree.list", treeIn{Repo: "git"}); err != nil {
				t.Fatalf("FAIL: detached repository still requires its import map: %v", err)
			}
			t.Log("ok: imported default, explicit history, metadata failures and detach")
		})
	}
}