package api
import (
"context"
"errors"
"path/filepath"
"strings"
"testing"
"craftdesign.group/quarry/internal/oltp"
"craftdesign.group/quarry/internal/ops"
)
// A repository is made in a team's namespace only by somebody who manages
// the team (RFC 0025), the rule team.repo.add already keeps: holding a
// team's name is not a way to put work under it.
func TestOnlyATeamsOwnerCreatesInIt(t *testing.T) {
db, err := oltp.Open(filepath.Join(t.TempDir(), "o.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { db.Close() })
for _, who := range []string{"ada", "bob"} {
if err := db.AddAccount(who, "a-long-enough-password", false); err != nil {
t.Fatal(err)
}
}
if err := db.AddTeam("crew", "Crew", "", "ada"); err != nil {
t.Fatal(err)
}
reg := &fakeRegistry{}
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(nil), Registry: reg,
Accounts: newFakeAccounts(), Teams: db}); err != nil {
t.Fatal(err)
}
as := func(who string) context.Context { return ops.WithUser(context.Background(), who) }
if _, err := r.Invoke(as("bob"), "repo.add", repoAddIn{Name: "widgets", Namespace: "crew", Create: true}); !errors.Is(err, ops.ErrForbidden) {
t.Errorf("FAIL: bob created in ada's team: %v", err)
}
if len(reg.added) != 0 {
t.Fatal("FAIL: a refused create reached the registry")
}
got, err := r.Invoke(as("bob"), "repo.name", repoNameIn{Name: "widgets", Namespace: "crew"})
if err != nil || got.(repoNameOut).Free {
t.Errorf("FAIL: the name check offered bob ada's team: %+v %v", got, err)
}
out, err := r.Invoke(as("ada"), "repo.add", repoAddIn{Name: "widgets", Namespace: "crew", Create: true})
if err != nil {
t.Fatalf("FAIL: ada could not create in her own team: %v", err)
}
// The registry places it in the team, before it takes its name.
if o := out.(repoAddOut); o.Namespace != "crew" || o.Handle != "widgets" ||
len(reg.added) != 1 || reg.added[0].Namespace != "crew" {
t.Errorf("FAIL: ada's repository is %+v; the registry was asked %+v", o, reg.added)
}
}
// What the forge sends a person names a repository by its address, not
// its key (RFC 0025): the label in text, the address in links, and a
// repoName beside the key every notification is routed by.
func TestWhatTheForgeSendsNamesARepositoryByItsAddress(t *testing.T) {
d := Deps{Origin: "https://forge.example", Address: func(repo string) (string, string, bool) {
switch repo {
case "rp-k2m9x7qa":
return "dan", "tools", true
case "tcl":
return "zeitforge", "tcl", true
}
return "", "", false
}}
if got := d.repoLabel("rp-k2m9x7qa"); got != "dan/tools" {
t.Errorf("FAIL: the minted repository is labelled %q", got)
}
for path, want := range map[string]string{
"/r/rp-k2m9x7qa/landing/abc": "https://forge.example/r/dan/tools/landing/abc",
"/r/tcl": "https://forge.example/r/zeitforge/tcl",
"/r/nonesuch/ticket/1": "https://forge.example/r/nonesuch/ticket/1",
"/me#notifications": "https://forge.example/me#notifications",
} {
if got := d.link(path); got != want {
t.Errorf("FAIL: link(%q) = %q, want %q", path, got, want)
}
}
e := NotifyEvent{Workflow: "quarry-landing", To: "ada", Payload: map[string]any{"repo": "rp-k2m9x7qa"}}
d.fireNotify(e)
if e.Payload["repoName"] != "dan/tools" || e.Payload["repo"] != "rp-k2m9x7qa" {
t.Errorf("FAIL: the payload reads %v", e.Payload)
}
if m, ok := moderationEvent("rp-k2m9x7qa", d.repoLabel("rp-k2m9x7qa"), "jan", "jva", "post", "rejected", "x", "", "l", "1"); !ok ||
!strings.Contains(m.Payload["title"].(string), "in dan/tools") {
t.Errorf("FAIL: the moderation notice says %v", m.Payload["title"])
}
}