Skip to content
quarry Artifact [316354188f]
Login

Artifact [316354188f]

Artifact 316354188fbdb955aa49e9277c88652145e017ed1f46d25adeb2d5ce5982d7c2:


package main

import (
	"errors"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"craftdesign.group/quarry/internal/api"
	"craftdesign.group/quarry/internal/oltp"
	"craftdesign.group/quarry/internal/ops"
)

// A failed import is a record, not a repository: its owner (or their
// agent) asking for the name again starts it over, and another account's
// attempt at the same name is in their own namespace and leaves it be.
// An import still copying holds its address.
func TestAFailedImportGivesItsNameBackToItsOwner(t *testing.T) {
	db, err := oltp.Open(filepath.Join(t.TempDir(), "oltp.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { db.Close() })
	for _, who := range []string{"dan", "eve"} {
		if err := db.AddAccount(who, "a-long-enough-password", false); err != nil {
			t.Fatal(err)
		}
	}
	if err := db.NewAgent("dan-bot", "dan", -1); err != nil {
		t.Fatal(err)
	}
	rg := &registrar{odb: db, deps: &api.Deps{Repos: api.NewRepos(nil)}, repoQuota: 25}
	importing := func(name, state string) {
		t.Helper()
		if err := db.AddImportingRepo(name, filepath.Join(t.TempDir(), name), "", oltp.Public, "https://example.com/"+name); err != nil {
			t.Fatal(err)
		}
		if err := db.SetOwner(name, "dan"); err != nil {
			t.Fatal(err)
		}
		if err := db.SetNamespace(name, "dan", "dan", time.Now()); err != nil {
			t.Fatal(err)
		}
		if state != oltp.Cloning {
			if err := db.SetRepoState(name, state, "import failed: Corrupt"); err != nil {
				t.Fatal(err)
			}
		}
	}
	importing("broke", oltp.Failed)
	importing("copying", oltp.Cloning)

	for _, c := range []struct {
		name, who string
		free      bool
	}{
		{"broke", "dan", true},
		{"broke", "dan-bot", true},
		// eve's broke is eve/broke, which dan's failed import does not
		// hold (RFC 0025: a name is unique within its namespace).
		{"broke", "eve", true},
		{"copying", "dan", false},
		{"fresh", "eve", true},
	} {
		err := rg.NameFree("", c.name, c.who)
		if c.free && err != nil {
			t.Errorf("FAIL: %s may not name %s: %v", c.who, c.name, err)
		}
		if !c.free && !errors.Is(err, ops.ErrInvalid) {
			t.Errorf("FAIL: %s may name %s: %v", c.who, c.name, err)
		}
	}

	// Somebody else's attempt leaves the record alone; the owner's clears it.
	if err := rg.retryFailedImport(api.RepoSpec{Name: "broke", Author: "eve"}); err != nil {
		t.Fatal(err)
	}
	if _, found, _ := db.RepoByName("broke"); !found {
		t.Fatal("FAIL: another account's attempt cleared the owner's failed import")
	}
	if err := rg.retryFailedImport(api.RepoSpec{Name: "broke", Author: "dan"}); err != nil {
		t.Fatal(err)
	}
	if _, found, _ := db.RepoByName("broke"); found {
		t.Error("FAIL: the owner's new attempt left the failed record holding the name")
	}
}

// A name that could be read as something else first in a path (the
// forge's namespace, an account) is still a fine name within a namespace
// (RFC 0025): it is the KEY that must not be, so such a repository gets
// a minted one.
func TestANameThatCannotBeAKeyGetsAMintedOne(t *testing.T) {
	db, err := oltp.Open(filepath.Join(t.TempDir(), "oltp.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { db.Close() })
	if err := db.AddAccount("dan", "a-long-enough-password", false); err != nil {
		t.Fatal(err)
	}
	rg := &registrar{odb: db, deps: &api.Deps{Repos: api.NewRepos(nil)}, repoQuota: 25}
	for _, name := range []string{oltp.ForgeNamespace, "ZeitForge", "dan", "new"} {
		if err := rg.NameFree("", name, "dan"); err != nil {
			t.Errorf("FAIL: %q is refused as a name within dan's namespace: %v", name, err)
		}
		key, err := rg.keyFor(name)
		if err != nil || key == name || !strings.HasPrefix(key, oltp.RepoKeyPrefix) {
			t.Errorf("FAIL: %q gets key %q (%v), want a minted one", name, key, err)
		}
	}
	if key, err := rg.keyFor("tools"); err != nil || key != "tools" {
		t.Errorf("FAIL: a free name gets key %q (%v), want itself", key, err)
	}
}

// An import a restart cut short tells its owner, who may have closed the
// progress page long before: the record says why, but only to somebody
// who goes and looks.
func TestAnInterruptedImportTellsItsOwner(t *testing.T) {
	db, err := oltp.Open(filepath.Join(t.TempDir(), "oltp.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { db.Close() })
	if err := db.AddAccount("dan", "a-long-enough-password", false); err != nil {
		t.Fatal(err)
	}
	if err := db.AddImportingRepo("linux", filepath.Join(t.TempDir(), "linux"), "", oltp.Public, "https://example.com/linux"); err != nil {
		t.Fatal(err)
	}
	if err := db.SetOwner("linux", "dan"); err != nil {
		t.Fatal(err)
	}
	told := make(sweepNotify, 4)
	rg := &registrar{odb: db, deps: &api.Deps{Repos: api.NewRepos(nil), Notify: told}, dir: t.TempDir()}
	rg.ReconcileImports()
	select {
	case e := <-told:
		if e.To != "dan" {
			t.Fatalf("FAIL: told %q, want the owner", e.To)
		}
		if link, _ := e.Payload["link"].(string); !strings.HasSuffix(link, "/new/progress/linux") {
			t.Errorf("FAIL: link = %q", link)
		}
	case <-time.After(5 * time.Second):
		t.Fatal("FAIL: the owner was not told")
	}
	if row, _, _ := db.RepoByName("linux"); row.State != oltp.Failed {
		t.Errorf("FAIL: state = %q", row.State)
	}
}

// A Git import the restart caught after it had published (in zit's cache
// warm-up) is whole: it is kept and served, not deleted with the partial
// imports. One that had not published is still removed and failed.
func TestARestartKeepsAGitImportThatHadPublished(t *testing.T) {
	db, err := oltp.Open(filepath.Join(t.TempDir(), "oltp.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { db.Close() })
	root := t.TempDir()
	published, partial := filepath.Join(root, "linux"), filepath.Join(root, "half")
	for name, path := range map[string]string{"linux": published, "half": partial} {
		if err := db.AddImportingRepo(name, path, "", oltp.Public, "https://example.com/"+name); err != nil {
			t.Fatal(err)
		}
		if err := os.MkdirAll(path, 0o700); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(filepath.Join(path, ".zit"), []byte("store"), 0o600); err != nil {
			t.Fatal(err)
		}
	}
	if err := os.WriteFile(filepath.Join(published, ".zit-import-map"), []byte("map"), 0o600); err != nil {
		t.Fatal(err)
	}
	rg := &registrar{odb: db, deps: &api.Deps{Repos: api.NewRepos(nil)}, dir: t.TempDir()}
	rg.ReconcileImports()

	if row, _, _ := db.RepoByName("linux"); row.State != oltp.Ready {
		t.Errorf("FAIL: the published import is %q, want ready", row.State)
	}
	if !gitImportPublished(published) {
		t.Error("FAIL: the published store was removed")
	}
	t.Log("ok: a published import is kept and ready")
	if row, _, _ := db.RepoByName("half"); row.State != oltp.Failed {
		t.Errorf("FAIL: the partial import is %q, want failed", row.State)
	}
	if _, err := os.Stat(partial); !os.IsNotExist(err) {
		t.Errorf("FAIL: the partial import's store was left (%v)", err)
	}
	t.Log("ok: a partial import is still removed and failed")
}