gitview/internal/routes/branding_test.go
internal/routes/branding_test.goRaw
1package routes
2
3import (
4 "fmt"
5 "net/http"
6 "net/http/httptest"
7 "os"
8 "path/filepath"
9 "regexp"
10 "strings"
11 "testing"
12
13 "gitview/internal/config"
14)
15
16// Verify configured branding renders consistently and resolves to the served asset.
17func TestConfiguredBranding(t *testing.T) {
18 c, _ := testWorkspace(t)
19 static := t.TempDir()
20 if err := os.MkdirAll(filepath.Join(static, "branding"), 0755); err != nil {
21 t.Fatal(err)
22 }
23 const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1"><circle r="1"/></svg>`
24 for _, name := range []string{"favicon.svg", "branding/owner mark.svg"} {
25 if err := os.WriteFile(filepath.Join(static, name), []byte(svg), 0644); err != nil {
26 t.Fatal(err)
27 }
28 }
29 favicon := regexp.MustCompile(`<link rel="icon" href="([^"]+)"`)
30 logo := regexp.MustCompile(`<img class="brand-mark" src="([^"]+)"`)
31 for _, tt := range []struct{ name, settings, asset string }{
32 {"logo overrides legacy favicon", " logo: branding/owner mark.svg\n favicon: nonexistent.svg\n", "/static/branding/owner%20mark.svg"},
33 {"legacy favicon", " favicon: branding/owner mark.svg\n", "/static/branding/owner%20mark.svg"},
34 {"default logo", "", "/static/favicon.svg"},
35 } {
36 t.Run(tt.name, func(t *testing.T) {
37 path := filepath.Join(t.TempDir(), "config.yaml")
38 yaml := fmt.Sprintf("repo:\n scanPath: %q\n mainBranch: [main]\ndirs:\n templates: %q\n static: %q\nmeta:\n username: arnaucube\n title: browser-only-title\n%s", c.Repo.ScanPath, c.Dirs.Templates, static, tt.settings)
39 if err := os.WriteFile(path, []byte(yaml), 0644); err != nil {
40 t.Fatal(err)
41 }
42 loaded, err := config.Read(path)
43 if err != nil {
44 t.Fatal(err)
45 }
46 handler := Handlers(loaded)
47 for _, page := range []struct {
48 url string
49 status int
50 }{{"/", 200}, {"/sample", 200}, {"/missing", 404}} {
51 w := httptest.NewRecorder()
52 handler.ServeHTTP(w, httptest.NewRequest("GET", page.url, nil))
53 if w.Code != page.status {
54 t.Fatalf("%s: status %d", page.url, w.Code)
55 }
56 body := w.Body.String()
57 icon := favicon.FindStringSubmatch(body)
58 brand := logo.FindStringSubmatch(body)
59 if len(icon) != 2 || len(brand) != 2 {
60 t.Fatalf("%s: missing favicon or logo", page.url)
61 }
62 if icon[1] != tt.asset || brand[1] != tt.asset {
63 t.Errorf("%s: favicon %q, logo %q; want %q", page.url, icon[1], brand[1], tt.asset)
64 }
65 _, header, found := strings.Cut(body, `<header class="topbar">`)
66 if !found {
67 t.Fatalf("%s: missing topbar", page.url)
68 }
69 topbar, _, _ := strings.Cut(header, "</header>")
70 if !strings.Contains(topbar, `class="brand-name">arnaucube</span>`) || strings.Contains(topbar, "browser-only-title") || strings.Contains(topbar, "top-context") {
71 t.Errorf("%s: topbar does not use only the configured username", page.url)
72 }
73 if !strings.Contains(body, "browser-only-title</title>") {
74 t.Errorf("%s: browser title was lost", page.url)
75 }
76 }
77 w := httptest.NewRecorder()
78 handler.ServeHTTP(w, httptest.NewRequest("GET", tt.asset, nil))
79 if w.Code != http.StatusOK || w.Body.String() != svg {
80 t.Fatalf("logo request: status %d, body %q", w.Code, w.Body.String())
81 }
82 if got := w.Header().Get("Content-Type"); !strings.HasPrefix(got, "image/svg+xml") {
83 t.Errorf("logo content type: %q", got)
84 }
85 loaded.Repo.ScanPath = filepath.Join(t.TempDir(), "missing")
86 w = httptest.NewRecorder()
87 handler.ServeHTTP(w, httptest.NewRequest("GET", "/", nil))
88 if w.Code != 500 || !strings.Contains(w.Body.String(), tt.asset) {
89 t.Errorf("server error page lost configured branding: status %d", w.Code)
90 }
91 })
92 }
93}