Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Blame · forge/instancerepo/instancerepo_test.go

Line attribution on main · 161 line(s)

6ce635a8
Keenan Nemetz
1d ago
1 package instancerepo
2
3 import (
4 "bytes"
5 "strings"
6 "testing"
7
d37d301f
Keenan Nemetz
16h ago
8 "rodent.software/vole/core/git"
6ce635a8
Keenan Nemetz
1d ago
9 )
10
11 func seed(t *testing.T, files map[string][]byte) (*git.Repo, string) {
12 t.Helper()
13 dir := t.TempDir()
14 if err := git.InitBare(dir, "main"); err != nil {
15 t.Fatal(err)
16 }
17 g := git.Open(dir)
18 tree, err := git.WriteFlatTree(g, files)
19 if err != nil {
20 t.Fatal(err)
21 }
22 commit, err := g.CommitTreeAs(tree, "", "seed", "test")
23 if err != nil {
24 t.Fatal(err)
25 }
26 return g, commit
27 }
28
29 func TestLoadPicksOnlyTheFixedPaths(t *testing.T) {
30 t.Parallel()
31 g, commit := seed(t, map[string][]byte{
32 "profile/README.md": []byte("# Public"),
33 "profile/private/README.md": []byte("# Private"),
34 "profile/custom.css": []byte("body {}"),
35 "profile/favicon.svg": []byte("<svg/>"),
36 "profile/notes.md": []byte("ignored"),
37 "assets/logo.png": []byte("PNG"),
38 "assets/readme.txt": []byte("not an asset"),
39 "README.md": []byte("notes"),
40 })
41 snap, err := load(g, commit)
42 if err != nil {
43 t.Fatal(err)
44 }
45 if string(snap.Readme) != "# Public" || string(snap.PrivateReadme) != "# Private" {
46 t.Fatalf("readmes = %q / %q", snap.Readme, snap.PrivateReadme)
47 }
48 if string(snap.CSS) != "body {}" || string(snap.Favicon) != "<svg/>" {
49 t.Fatalf("css/favicon = %q / %q", snap.CSS, snap.Favicon)
50 }
51 if len(snap.Assets) != 1 || string(snap.Assets["logo.png"].Body) != "PNG" {
52 t.Fatalf("assets = %+v", snap.Assets)
53 }
54 }
55
d222b0cf
Keenan Nemetz
1d ago
56 func TestLoadReadsThemesFromFileNames(t *testing.T) {
f7a34d87
Keenan Nemetz
1d ago
57 t.Parallel()
58 g, commit := seed(t, map[string][]byte{
d222b0cf
Keenan Nemetz
1d ago
59 "profile/custom.css": []byte(":root[data-theme] { --radius: 2px; }"),
f7a34d87
Keenan Nemetz
1d ago
60 "profile/themes/acme-paper.css": []byte(`:root[data-theme="acme-paper"] { color-scheme: light; }`),
d222b0cf
Keenan Nemetz
1d ago
61 "profile/themes/acme-ocean.css": []byte(`:root[data-theme="acme-ocean"] { color-scheme: dark; }`),
62 // An old manifest from before the file-name convention is ignored, and
63 // a non-CSS file under the directory is not a theme.
64 "profile/themes/index.json": []byte(`{"themes":[{"id":"gone","name":"Gone"}]}`),
65 "profile/themes/notes.txt": []byte("ignored"),
f7a34d87
Keenan Nemetz
1d ago
66 })
67 snap, err := load(g, commit)
68 if err != nil {
69 t.Fatal(err)
70 }
71 if len(snap.Themes) != 2 {
72 t.Fatalf("themes = %+v, want two", snap.Themes)
73 }
74 if snap.Themes[0].ID != "acme-ocean" || snap.Themes[0].Name != "Acme Ocean" ||
75 !strings.Contains(string(snap.Themes[0].CSS), `data-theme="acme-ocean"`) {
76 t.Fatalf("first theme = %+v", snap.Themes[0])
77 }
78 if snap.Themes[1].ID != "acme-paper" || snap.Themes[1].Name != "Acme Paper" {
79 t.Fatalf("second theme = %+v", snap.Themes[1])
80 }
81 if string(snap.CSS) != ":root[data-theme] { --radius: 2px; }" {
82 t.Fatalf("custom.css = %q", snap.CSS)
83 }
84 }
85
d222b0cf
Keenan Nemetz
1d ago
86 func TestLoadRejectsABadThemeFileName(t *testing.T) {
f7a34d87
Keenan Nemetz
1d ago
87 t.Parallel()
88 cases := map[string]map[string][]byte{
89 "reserved id": {
90 "profile/themes/everforest-dark.css": []byte("body {}"),
91 },
d222b0cf
Keenan Nemetz
1d ago
92 "system id": {
93 "profile/themes/system.css": []byte("body {}"),
f7a34d87
Keenan Nemetz
1d ago
94 },
0584bb70
Keenan Nemetz
1d ago
95 "nord id": {
96 "profile/themes/nord.css": []byte("body {}"),
97 },
98 "catppuccin prefix": {
99 "profile/themes/catppuccin-custom.css": []byte("body {}"),
100 },
101 "solarized prefix": {
102 "profile/themes/solarized-custom.css": []byte("body {}"),
103 },
97b3b022
Keenan Nemetz
1d ago
104 "dracula id": {
105 "profile/themes/dracula.css": []byte("body {}"),
106 },
107 "tokyo night id": {
108 "profile/themes/tokyo-night.css": []byte("body {}"),
109 },
110 "rose pine prefix": {
111 "profile/themes/rose-pine-custom.css": []byte("body {}"),
112 },
113 "kanagawa prefix": {
114 "profile/themes/kanagawa-custom.css": []byte("body {}"),
115 },
116 "ayu prefix": {
117 "profile/themes/ayu-custom.css": []byte("body {}"),
118 },
f7a34d87
Keenan Nemetz
1d ago
119 "invalid id": {
120 "profile/themes/Acme Ocean.css": []byte("body {}"),
121 },
d222b0cf
Keenan Nemetz
1d ago
122 "uppercase id": {
123 "profile/themes/Acme.css": []byte("body {}"),
f7a34d87
Keenan Nemetz
1d ago
124 },
125 }
126 for name, files := range cases {
127 name, files := name, files
128 t.Run(name, func(t *testing.T) {
129 t.Parallel()
130 g, commit := seed(t, files)
131 if _, err := load(g, commit); err == nil {
d222b0cf
Keenan Nemetz
1d ago
132 t.Fatal("load accepted a bad theme file name")
f7a34d87
Keenan Nemetz
1d ago
133 }
134 })
135 }
136 }
137
6ce635a8
Keenan Nemetz
1d ago
138 func TestLoadRejectsAnOversizedFile(t *testing.T) {
139 t.Parallel()
140 g, commit := seed(t, map[string][]byte{
141 "assets/big.png": bytes.Repeat([]byte("x"), maxFileBytes+1),
142 })
143 if _, err := load(g, commit); err == nil || !strings.Contains(err.Error(), "over the") {
144 t.Fatalf("oversized asset = %v", err)
145 }
146 }
147
148 func TestAllowedAsset(t *testing.T) {
149 t.Parallel()
150 cases := map[string]bool{
151 "assets/logo.png": true, "assets/logo.PNG": true, "assets/font.woff2": true,
152 "assets/icon.svg": true, "assets/site.css": true,
153 "assets/app.js": false, "assets/page.html": false, "assets/x.mjs": false,
154 "assets/notes.txt": false, "assets/archive.zip": false,
155 }
156 for name, want := range cases {
157 if got := allowedAsset(name); got != want {
158 t.Fatalf("allowedAsset(%q) = %v, want %v", name, got, want)
159 }
160 }
161 }