Skip to content
quarry Artifact [1964cb1ae3]
Login

Artifact [1964cb1ae3]

Artifact 1964cb1ae30ba25d4dd08bc61449da24d917b8320b2f56b0070468340b829ed3:


package xfer

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

// seen records what the gate tallied, in order.
type seen struct {
	mu sync.Mutex
	r  []Reader
}

func (s *seen) add(_ string, r Reader) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.r = append(s.r, r)
}

func (s *seen) all() []Reader {
	s.mu.Lock()
	defer s.mu.Unlock()
	return append([]Reader(nil), s.r...)
}

// A real clone through the real gate: the tally has to survive the whole
// path — container decoding, card scanning, the User-Agent — and not
// just the unit tests' hand-built bodies.
func TestARealCloneIsTalliedWithItsClient(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("# counted\n"), 0o644); err != nil {
		t.Fatal(err)
	}
	zrun(t, origin, "add", "README.md")
	zrun(t, origin, "commit", "-m", "first")

	var got seen
	f := New()
	defer f.Close()
	f.Register("alpha", origin)
	f.SetCounted(got.add)
	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, "clone"))

	rs := got.all()
	if len(rs) == 0 {
		t.Fatal("FAIL: a real clone tallied nothing")
	}
	clones := 0
	for _, r := range rs {
		if r.Clone {
			clones++
			if r.Client != "zit" {
				t.Errorf("FAIL: a zit clone was recorded as client %q", r.Client)
			}
			// The version is self-reported and its value is zit's
			// business, but a clone that carried a pragma must not
			// record an empty one.
			if r.Version == "" {
				t.Error("FAIL: a clone carrying a client-version pragma recorded no version")
			}
			if !r.Anonymous() {
				t.Errorf("FAIL: an anonymous clone named %q/%q", r.Account, r.Claimed)
			}
		}
	}
	if clones != 1 {
		t.Errorf("FAIL: %d clones tallied for one clone, want 1", clones)
	}
}

// The one property this whole change must not break: identity resolution
// runs AFTER the response, so a credential that does not check out costs
// a name off a statistic and never a clone. A public repository cloned
// with a stale password works today and has to keep working.
func TestABadCredentialStillServesTheClone(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("# open\n"), 0o644); err != nil {
		t.Fatal(err)
	}
	zrun(t, origin, "add", "README.md")
	zrun(t, origin, "commit", "-m", "first")

	var got seen
	f := New()
	defer f.Close()
	f.Register("alpha", origin)
	f.SetCounted(got.add)
	// An Identify that refuses everybody, standing in for a stale
	// password, a renamed account, or a database that was busy.
	f.SetIdentify(func(*http.Request) string { return "" })
	mux := http.NewServeMux()
	mux.Handle("POST /r/{repo}", f)
	srv := httptest.NewServer(mux)
	defer srv.Close()

	work := t.TempDir()
	clone := filepath.Join(work, "clone")
	zrun(t, work, "clone", strings.Replace(srv.URL, "http://", "http://ghost:wrong@", 1)+"/r/alpha", clone)
	if tl := zrun(t, clone, "timeline", "-n", "3"); !strings.Contains(tl, "first") {
		t.Fatalf("FAIL: a bad credential cost the clone its history:\n%s", tl)
	}
	for _, r := range got.all() {
		if r.Account != "" {
			t.Errorf("FAIL: a credential nothing verified was recorded as account %q", r.Account)
		}
	}
}