Skip to content
quarry Artifact [cb8f34098b]
Login

Artifact [cb8f34098b]

Artifact cb8f34098b0ee730dcddc1ab88eba786a902d9ee47a8eb7d3edc211914a20c08:


package xfer

import (
	"net/http"
	"net/http/httptest"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

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

// TestCloneAndPushThroughFronter is the 0001 §7 acceptance: `zit clone
// <quarry>/r/<repo>` works, and a push through the same URL lands
// artifacts in the fronted store.
func TestCloneAndPushThroughFronter(t *testing.T) {
	if _, err := exec.LookPath("zit"); err != nil {
		t.Skip("zit not on PATH")
	}
	origin := t.TempDir()
	zrun(t, origin, "init")
	if err := os.WriteFile(filepath.Join(origin, "README.md"), []byte("# fronted\n"), 0o644); err != nil {
		t.Fatal(err)
	}
	zrun(t, origin, "add", "README.md")
	zrun(t, origin, "commit", "-m", "first")

	f := New()
	defer f.Close()
	f.Register("alpha", origin)
	mux := http.NewServeMux()
	mux.Handle("POST /r/{repo}", f)
	mux.Handle("POST /r/{repo}/xfer", f)
	srv := httptest.NewServer(mux)
	defer srv.Close()

	// Clone through quarry: the repo URL is used verbatim by zit.
	work := t.TempDir()
	clone := filepath.Join(work, "clone")
	zrun(t, work, "clone", srv.URL+"/r/alpha", clone)
	if tl := zrun(t, clone, "timeline", "-n", "3"); !strings.Contains(tl, "first") {
		t.Fatalf("clone missing history:\n%s", tl)
	}

	// Push back through the same URL (saved as the clone's remote).
	if err := os.WriteFile(filepath.Join(clone, "NOTES.md"), []byte("pushed\n"), 0o644); err != nil {
		t.Fatal(err)
	}
	zrun(t, clone, "add", "NOTES.md")
	zrun(t, clone, "commit", "-m", "pushed through quarry")
	zrun(t, clone, "push")
	if tl := zrun(t, origin, "timeline", "-n", "3"); !strings.Contains(tl, "pushed through quarry") {
		t.Fatalf("push did not land in origin:\n%s", tl)
	}

	// The /xfer alias answers too.
	zrun(t, work, "clone", srv.URL+"/r/alpha/xfer", filepath.Join(work, "clone2"))

	// Unknown repos 404 rather than 502.
	resp, err := http.Post(srv.URL+"/r/nope", "application/x-fossil", strings.NewReader(""))
	if err != nil {
		t.Fatal(err)
	}
	resp.Body.Close()
	if resp.StatusCode != http.StatusNotFound {
		t.Fatalf("unknown repo: status %d", resp.StatusCode)
	}
}

// TestBackendRestartAfterDeath: a dead child is restarted on the next
// request instead of 502ing forever.
func TestBackendRestartAfterDeath(t *testing.T) {
	if _, err := exec.LookPath("zit"); err != nil {
		t.Skip("zit not on PATH")
	}
	origin := t.TempDir()
	zrun(t, origin, "init")

	f := New()
	defer f.Close()
	f.Register("alpha", origin)
	mux := http.NewServeMux()
	mux.Handle("POST /r/{repo}", f)
	srv := httptest.NewServer(mux)
	defer srv.Close()

	work := t.TempDir()
	zrun(t, work, "clone", srv.URL+"/r/alpha", filepath.Join(work, "c1"))

	// Kill the child behind its back.
	f.mu.Lock()
	b := f.repos["alpha"]
	b.cmd.Process.Kill()
	f.mu.Unlock()

	zrun(t, work, "clone", srv.URL+"/r/alpha", filepath.Join(work, "c2"))
}