e2e/quota_test.go

ff759b53942049b2043e132f5482b442a7265b98
gitbay/e2e/quota_test.go history · blame · raw

108 lines · 4641 bytes

  1package e2e
  2
  3import (
  4	"crypto/rand"
  5	"fmt"
  6	"os"
  7	"path/filepath"
  8	"strings"
  9	"testing"
 10	"time"
 11)
 12
 13// Per-account caps on repositories and storage, with the admin override,
 14// and expiry of accounts that never verified.
 15func TestQuotasAndPendingExpiry(t *testing.T) {
 16	t.Setenv("GITBAY_REAP_TICK", "500ms")
 17	smtp := startFakeSMTP(t)
 18	inst := startInstanceWith(t, fmt.Sprintf(
 19		"[registration]\nmode = \"open\"\npending_expiry = \"2s\"\n[mail]\nsmtp_host = %q\nfrom = \"noreply@gitbay.test\"\n"+
 20			"[limits]\nmax_repos_per_user = 2\nmax_bytes_per_user = 300000\n", smtp.addr))
 21	rootKey := inst.newKey(t, "root")
 22	aliceKey := inst.newKey(t, "alice")
 23	inst.admin(t, "admin", "user", "create", "root", "--key", rootKey+".pub", "--admin")
 24	inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
 25
 26	// Two repositories fit; the third is refused with the numbers.
 27	for _, r := range []string{"alice/one", "alice/two"} {
 28		if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", r); code != 0 {
 29			t.Fatalf("create %s: %s", r, errOut)
 30		}
 31	}
 32	if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/three"); code != 4 || !strings.Contains(errOut, "2 of the 2 repositories") {
 33		t.Fatalf("third repo: exit %d %s", code, errOut)
 34	}
 35	if _, _, code := inst.ssh(t, aliceKey, "", "repo", "fork", "alice/one", "--name", "onefork"); code != 4 {
 36		t.Fatal("fork slipped past the cap")
 37	}
 38	// An org is not capped.
 39	if _, _, code := inst.ssh(t, aliceKey, "", "org", "create", "acme"); code != 0 {
 40		t.Fatal("org create failed")
 41	}
 42	if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "acme/lib"); code != 0 {
 43		t.Fatalf("org repo: %s", errOut)
 44	}
 45	// The admin raises the cap for this account; the third fits.
 46	if out, _, code := inst.ssh(t, rootKey, "", "admin", "user", "limits", "alice", "--repos", "3"); code != 0 || !strings.Contains(out, "repos 2 of 3") {
 47		t.Fatalf("limits: exit %d %s", code, out)
 48	}
 49	if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/three"); code != 0 {
 50		t.Fatalf("third repo after raise: %s", errOut)
 51	}
 52	if out, _, _ := inst.ssh(t, rootKey, "", "admin", "user", "show", "alice", "--json"); !strings.Contains(out, `"repo_limit":3`) || !strings.Contains(out, `"byte_limit":300000`) {
 53		t.Fatalf("show lacks limits:\n%s", out)
 54	}
 55	if out, _, _ := inst.ssh(t, rootKey, "", "admin", "user", "limits", "alice", "--repos", "default"); !strings.Contains(out, "of 2") {
 56		t.Fatalf("limits back to default:\n%s", out)
 57	}
 58
 59	// Storage: a push past what the account has left is refused.
 60	work := t.TempDir()
 61	env := inst.gitEnv(aliceKey)
 62	mustGit(t, work, env, "clone", inst.sshURL("alice/one"), "w")
 63	dir := filepath.Join(work, "w")
 64	os.WriteFile(filepath.Join(dir, "small.txt"), []byte("ok\n"), 0o644)
 65	mustGit(t, dir, env, "checkout", "-q", "-b", "main")
 66	mustGit(t, dir, env, "add", ".")
 67	mustGit(t, dir, env, "commit", "-q", "-m", "small")
 68	mustGit(t, dir, env, "push", "-q", "origin", "main")
 69	big := make([]byte, 400_000)
 70	rand.Read(big)
 71	os.WriteFile(filepath.Join(dir, "big.bin"), big, 0o644)
 72	mustGit(t, dir, env, "add", ".")
 73	mustGit(t, dir, env, "commit", "-q", "-m", "big")
 74	if out, code := gitRun(t, dir, env, "push", "origin", "main"); code == 0 || !strings.Contains(out, "max") {
 75		t.Fatalf("push past the storage cap accepted: exit %d\n%s", code, out)
 76	}
 77	if _, _, code := inst.ssh(t, rootKey, "", "admin", "user", "limits", "alice", "--bytes", "0"); code != 0 {
 78		t.Fatal("lift byte cap failed")
 79	}
 80	mustGit(t, dir, env, "push", "-q", "origin", "main")
 81
 82	// An account that registers and never verifies is removed after
 83	// pending_expiry; the name is free again.
 84	newKey := inst.newKey(t, "dana")
 85	if _, errOut, code := inst.ssh(t, newKey, "", "register", "--username", "dana", "--email", "dana@example.test"); code != 0 {
 86		t.Fatalf("register: %s", errOut)
 87	}
 88	if out, _, _ := inst.ssh(t, rootKey, "", "admin", "user", "list", "--state", "pending"); !strings.HasPrefix(out, "dana\t") {
 89		t.Fatalf("dana not pending:\n%s", out)
 90	}
 91	deadline := time.Now().Add(15 * time.Second)
 92	for {
 93		out, _, _ := inst.ssh(t, rootKey, "", "admin", "user", "list", "--state", "pending")
 94		if strings.TrimSpace(out) == "" {
 95			break
 96		}
 97		if time.Now().After(deadline) {
 98			t.Fatalf("pending account never expired:\n%s", out)
 99		}
100		time.Sleep(300 * time.Millisecond)
101	}
102	if _, _, code := inst.ssh(t, newKey, "", "whoami"); code == 0 {
103		t.Fatal("expired account still authenticates")
104	}
105	if out := inst.admin(t, "admin", "audit", "--action", "pending.expired"); !strings.Contains(out, `"user":"dana"`) {
106		t.Fatalf("expiry not audited:\n%s", out)
107	}
108}