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.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 2h ago 4b8c9e1d
history blame raw 4.5 KiB
1package config
2
3import (
4 "os"
5 "path/filepath"
6 "testing"
7)
8
9// TestDefaultPathFindsThePerUserInstance is the flow `vole admin init --dir
10// ~/.vole` promises: later commands find that config without --config.
11func TestDefaultPathFindsThePerUserInstance(t *testing.T) {
12 home := t.TempDir()
13 t.Setenv("HOME", home)
14 t.Setenv("USERPROFILE", home)
15 t.Setenv("VOLE_CONFIG", "")
16 t.Chdir(t.TempDir())
17
18 if got := DefaultPath(); got != "vole.toml" {
19 t.Fatalf("default path with no instance = %q, want vole.toml", got)
20 }
21
22 homeConfig := filepath.Join(home, ".vole", "vole.toml")
23 if err := os.MkdirAll(filepath.Dir(homeConfig), 0o755); err != nil {
24 t.Fatal(err)
25 }
26 if err := os.WriteFile(homeConfig, []byte("data_dir = \"/tmp/vole-data\"\n"), 0o644); err != nil {
27 t.Fatal(err)
28 }
29 if got := DefaultPath(); got != homeConfig {
30 t.Fatalf("default path = %q, want %q", got, homeConfig)
31 }
32
33 // A config in the working directory belongs to the checkout or instance the
34 // operator is standing in, so it wins over the home instance.
35 if err := os.WriteFile("vole.toml", []byte("data_dir = \"./data\"\n"), 0o644); err != nil {
36 t.Fatal(err)
37 }
38 if got := DefaultPath(); got != "vole.toml" {
39 t.Fatalf("local config did not win: %q", got)
40 }
41
42 // An explicit environment choice wins over both.
43 t.Setenv("VOLE_CONFIG", "/srv/vole/vole.toml")
44 if got := DefaultPath(); got != "/srv/vole/vole.toml" {
45 t.Fatalf("VOLE_CONFIG did not win: %q", got)
46 }
47}
48
49// TestResolvePathKeepsExplicitChoice: an empty path means discover, but a named
50// path must never be silently replaced.
51func TestResolvePathKeepsExplicitChoice(t *testing.T) {
52 t.Setenv("VOLE_CONFIG", "/from/env.toml")
53 if got := ResolvePath("/explicit/vole.toml"); got != "/explicit/vole.toml" {
54 t.Fatalf("ResolvePath explicit = %q", got)
55 }
56 if got := ResolvePath(""); got != "/from/env.toml" {
57 t.Fatalf("ResolvePath empty = %q", got)
58 }
59}
60
61// A mistyped TOML tag does not fail anything: the key is simply ignored and the
62// default applies, which is invisible until somebody's cap does nothing. So the one
63// setting whose absence is a *silent* policy change is pinned here.
64func TestMaxJobsPerRepoLoads(t *testing.T) {
65 t.Parallel()
66 path := filepath.Join(t.TempDir(), "vole.toml")
67 if err := os.WriteFile(path, []byte("max_jobs_per_repo = 3\n"), 0o644); err != nil {
68 t.Fatal(err)
69 }
70 c, err := Load(path)
71 if err != nil {
72 t.Fatal(err)
73 }
74 if c.MaxJobsPerRepo != 3 {
75 t.Fatalf("MaxJobsPerRepo = %d, want 3", c.MaxJobsPerRepo)
76 }
77
78 // Absent means no bound, which is the behaviour before the key existed.
79 unset, err := Load("")
80 if err != nil {
81 t.Fatal(err)
82 }
83 if unset.MaxJobsPerRepo != 0 {
84 t.Fatalf("default = %d, want 0 (no cap)", unset.MaxJobsPerRepo)
85 }
86}
87
88// Automatic HTTPS is opted into by naming an address; the rest of the settings
89// only matter once that switch is on, so pin their TOML names as a group.
90func TestACMESettingsLoad(t *testing.T) {
91 t.Parallel()
92 path := filepath.Join(t.TempDir(), "vole.toml")
93 body := `https_addr = ":443"
94acme_email = "admin@example.com"
95acme_domains = ["forge.example.com", "git.example.com"]
96acme_cache_dir = "/var/lib/vole/acme"
97acme_directory_url = "https://acme-staging-v02.api.letsencrypt.org/directory"
98`
99 if err := os.WriteFile(path, []byte(body), 0o644); err != nil {
100 t.Fatal(err)
101 }
102 c, err := Load(path)
103 if err != nil {
104 t.Fatal(err)
105 }
106 if c.HTTPSAddr != ":443" {
107 t.Fatalf("HTTPSAddr = %q", c.HTTPSAddr)
108 }
109 if c.ACMEEmail != "admin@example.com" {
110 t.Fatalf("ACMEEmail = %q", c.ACMEEmail)
111 }
112 if got := len(c.ACMEDomains); got != 2 || c.ACMEDomains[0] != "forge.example.com" {
113 t.Fatalf("ACMEDomains = %v", c.ACMEDomains)
114 }
115 if got := c.ACMECachePath(); got != "/var/lib/vole/acme" {
116 t.Fatalf("ACMECachePath = %q", got)
117 }
118 if c.ACMEDirectoryURL != "https://acme-staging-v02.api.letsencrypt.org/directory" {
119 t.Fatalf("ACMEDirectoryURL = %q", c.ACMEDirectoryURL)
120 }
121}
122
123// Load records whether it actually read a file, so the status page can tell an
124// operator whether the effective values came from disk or the built-in defaults.
125func TestLoadRecordsTheSource(t *testing.T) {
126 t.Parallel()
127 path := filepath.Join(t.TempDir(), "vole.toml")
128 if err := os.WriteFile(path, []byte("data_dir = \"/srv/vole\"\n"), 0o644); err != nil {
129 t.Fatal(err)
130 }
131 c, err := Load(path)
132 if err != nil {
133 t.Fatal(err)
134 }
135 if !c.Loaded || c.Path != path {
136 t.Fatalf("loaded config = %+v, want Loaded with path %q", c, path)
137 }
138
139 missing, err := Load(filepath.Join(t.TempDir(), "missing.toml"))
140 if err != nil {
141 t.Fatal(err)
142 }
143 if missing.Loaded {
144 t.Fatal("a missing config file was reported as loaded")
145 }
146}
147