krz/gitbay

A CLI-first git forge.

clone: git clone https://gitbay.org/krz/gitbay.git

repo-descriptions: internal/httpd/routes_test.go · raw

 1package httpd
 2
 3import (
 4	"strings"
 5	"testing"
 6
 7	"gitbay.org/gitbay/internal/config"
 8	"gitbay.org/gitbay/internal/policy"
 9)
10
11// TestViewOnlyHasNoMutatingRoutes is the structural guarantee from the plan:
12// under web.mode = "view_only" the route table must contain no mutating
13// route — not hidden ones, none at all.
14func TestViewOnlyHasNoMutatingRoutes(t *testing.T) {
15	cfg := config.Default()
16	cfg.Web.Mode = "view_only"
17	s := New(cfg, nil)
18
19	for _, r := range s.Routes() {
20		if r.Mutating {
21			t.Errorf("view_only route table contains mutating route %s %s", r.Method, r.Pattern)
22		}
23		// The only POSTs allowed are the git transport endpoints: a pure
24		// read (upload-pack) and a static refusal (receive-pack).
25		if r.Method != "GET" && !strings.Contains(r.Pattern, "git-upload-pack") && !strings.Contains(r.Pattern, "git-receive-pack") {
26			t.Errorf("view_only route table contains non-GET route %s %s", r.Method, r.Pattern)
27		}
28		for _, word := range []string{"login", "logout", "register", "edit", "new", "settings"} {
29			if strings.Contains(r.Pattern, "/"+word) {
30				t.Errorf("view_only route table contains account-mode pattern %s %s", r.Method, r.Pattern)
31			}
32		}
33	}
34}
35
36// TestAPIRouteGating: the API route exists only when [api] enabled = true.
37func TestAPIRouteGating(t *testing.T) {
38	has := func(cfg config.Config) bool {
39		for _, r := range New(cfg, nil).Routes() {
40			if r.Pattern == "/api/v1/cmd" {
41				return true
42			}
43		}
44		return false
45	}
46	if has(config.Default()) {
47		t.Fatal("API route present with api disabled (the default)")
48	}
49	cfg := config.Default()
50	cfg.API.Enabled = true
51	if !has(cfg) {
52		t.Fatal("API route missing with api enabled")
53	}
54}
55
56// TestAccountsModeHasLoginRoute is the positive counterpart: switching the
57// mode on registers the session routes.
58func TestAccountsModeHasLoginRoute(t *testing.T) {
59	cfg := config.Default()
60	cfg.Web.Mode = "accounts"
61	s := New(cfg, nil)
62	found := false
63	for _, r := range s.Routes() {
64		if r.Pattern == "/login" {
65			found = true
66		}
67	}
68	if !found {
69		t.Fatal("accounts mode is missing the /login route")
70	}
71}
72
73// TestTopLevelRouteWordsAreReserved keeps the route table and the reserved
74// username list in agreement: every literal first path segment must be an
75// unclaimable username.
76func TestTopLevelRouteWordsAreReserved(t *testing.T) {
77	cfg := config.Default()
78	cfg.Web.Mode = "accounts" // superset of routes
79	s := New(cfg, nil)
80	for _, r := range s.Routes() {
81		seg := strings.TrimPrefix(r.Pattern, "/")
82		seg, _, _ = strings.Cut(seg, "/")
83		if seg == "" || strings.HasPrefix(seg, "{") {
84			continue // wildcard or root
85		}
86		if !policy.Reserved(seg) {
87			t.Errorf("top-level route word %q is not in the reserved username list", seg)
88		}
89	}
90}