Skip to content
quarry Artifact [c0878cbcc1]
Login

Artifact [c0878cbcc1]

Artifact c0878cbcc1ffcceb54ddd2d12e5bc42e6145be50cddf1f9c87f1a62377209a3c:


package main

import (
	"net/http"
	"net/http/httptest"
	"path/filepath"
	"strings"
	"sync/atomic"
	"testing"

	"craftdesign.group/quarry/internal/web"
)

// No repository name is reserved: a repository named pickup, at one
// segment or under a namespace, still clones, pulls and pushes with real
// zit through the pickup front, none of it reaches the collector, and a
// request presenting the Pickup scheme does, at the syntactic route.
func TestARepositoryNamedPickupStillSyncs(t *testing.T) {
	r := newFirstPushRig(t, "pickup")
	var collected atomic.Int64
	var gotRepo atomic.Value
	collector := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		collected.Add(1)
		gotRepo.Store(req.PathValue("repo"))
		w.WriteHeader(http.StatusNoContent)
	})
	mux := http.NewServeMux()
	mux.Handle("POST /r/{repo}", r.front)
	// The namespace rewrite, as the page layer does it: /r/alice/x is
	// the flat /r/x.
	mux.HandleFunc("POST /r/alice/{repo}", func(w http.ResponseWriter, req *http.Request) {
		inner := req.Clone(req.Context())
		inner.URL.Path = "/r/" + req.PathValue("repo")
		inner.SetPathValue("repo", req.PathValue("repo"))
		r.front.ServeHTTP(w, inner)
	})
	srv := httptest.NewServer(web.PickupFront(mux, collector))
	defer srv.Close()
	flat, ns := srv.URL+"/r/pickup", srv.URL+"/r/alice/pickup"
	user := []string{"--user", "jan", "--password", r.token}
	proj := r.project(t, 1)
	if out, err := r.zit(t, proj, append([]string{"push", flat}, user...)...); err != nil {
		t.Fatalf("FAIL: push to the one-segment URL: %v\n%s", err, out)
	}
	cl := filepath.Join(t.TempDir(), "clone")
	if out, err := r.zit(t, ".", append([]string{"clone", ns, cl}, user...)...); err != nil {
		t.Fatalf("FAIL: clone from the namespaced URL: %v\n%s", err, out)
	}
	r.commit(t, cl)
	if out, err := r.zit(t, cl, append([]string{"push", ns}, user...)...); err != nil {
		t.Fatalf("FAIL: push to the namespaced URL: %v\n%s", err, out)
	}
	if out, err := r.zit(t, proj, append([]string{"pull", flat}, user...)...); err != nil {
		t.Fatalf("FAIL: pull from the one-segment URL: %v\n%s", err, out)
	}
	if got, want := r.checkins(t, proj), r.checkins(t, cl); got != want {
		t.Errorf("FAIL: the pulled checkout has %d timeline lines, want %d", got, want)
	}
	if n := collected.Load(); n != 0 {
		t.Errorf("FAIL: %d syncs reached the collector", n)
	}
	// A sync-shaped POST at the namespaced path, with Basic or nothing,
	// never reaches it either.
	for _, auth := range []string{"", "Basic amFuOnB3"} {
		req, _ := http.NewRequest("POST", ns, strings.NewReader("x"))
		req.Header.Set("Content-Type", "application/x-fossil")
		if auth != "" {
			req.Header.Set("Authorization", auth)
		}
		if resp, err := http.DefaultClient.Do(req); err == nil {
			resp.Body.Close()
		}
	}
	if n := collected.Load(); n != 0 {
		t.Errorf("FAIL: a non-pickup POST reached the collector")
	}
	// A pickup is read syntactically: the last segment is the route.
	for _, path := range []string{"/r/alice/pickup/pickup", "/r/pickup/pickup"} {
		req, _ := http.NewRequest("POST", srv.URL+path, nil)
		req.Header.Set("Authorization", "Pickup abc")
		resp, err := http.DefaultClient.Do(req)
		if err != nil || resp.StatusCode != http.StatusNoContent {
			t.Errorf("FAIL: %s did not reach the collector: %v", path, err)
		}
		if resp != nil {
			resp.Body.Close()
		}
	}
	if collected.Load() != 2 || gotRepo.Load() == "" {
		t.Errorf("FAIL: collector calls %d, repo %v", collected.Load(), gotRepo.Load())
	}
	if !t.Failed() {
		t.Log("ok")
	}
}

// Through the real handler, a POST that does not present the Pickup
// scheme is never the collector's, whatever the path; one that does is,
// at /r/{ns}/pickup/pickup as at any other address.
func TestOnlyAPickupSchemeReachesTheCollector(t *testing.T) {
	g := newSiteRig(t)
	n := 0
	do := func(path, auth string) *httptest.ResponseRecorder {
		n++
		req := httptest.NewRequest("POST", path, strings.NewReader("x"))
		req.RemoteAddr = "192.0.2." + string(rune('0'+n)) + ":1"
		if auth != "" {
			req.Header.Set("Authorization", auth)
		}
		w := httptest.NewRecorder()
		g.h.ServeHTTP(w, req)
		return w
	}
	for _, path := range []string{"/r/alice/pickup", "/r/pickup", "/r/alice/pub/pickup"} {
		for _, auth := range []string{"", "Basic amFuOnB3", "Bearer abc"} {
			if w := do(path, auth); strings.Contains(w.Body.String(), "no such pickup") {
				t.Errorf("FAIL: %s with %q reached the collector", path, auth)
			}
		}
	}
	for _, path := range []string{"/r/alice/pickup/pickup", "/r/pickup/pickup"} {
		if w := do(path, "Pickup AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); w.Code != 404 || !strings.Contains(w.Body.String(), "no such pickup") {
			t.Errorf("FAIL: %s with the Pickup scheme: %d %s", path, w.Code, w.Body)
		}
	}
	if !t.Failed() {
		t.Log("ok")
	}
}