package main
import (
"errors"
"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 := ®istrar{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 := ®istrar{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)
}
}