1package config
2
3import (
4 "os"
5 "path/filepath"
6 "reflect"
7 "testing"
8)
9
10const presetsFixtureTOML = `
11[global]
12verbose = true
13
14[global.backup]
15exclude-file = "/etc/excludes"
16
17[home]
18repo = "/repos/home"
19
20[home.backup]
21_arguments = ["/home/alice"]
22
23["@cloud"]
24repo = "/repos/cloud"
25
26["@cloud".environ]
27AWS_PROFILE = "restic"
28
29["home@"]
30host = "laptop"
31
32["home@cloud"]
33repo = "/repos/home-cloud"
34
35[archive]
36json = true
37
38[vars]
39cache-root = "/srv"
40`
41
42func TestPresetsFrom(t *testing.T) {
43 t.Parallel()
44
45 dir := t.TempDir()
46 path := filepath.Join(dir, "config.toml")
47 if err := os.WriteFile(path, []byte(presetsFixtureTOML), 0o600); err != nil {
48 t.Fatalf("writing fixture: %v", err)
49 }
50
51 got := presetsFrom([]string{path})
52
53 // Plain presets first, then prefixĂ—suffix combos, then explicit full
54 // presets (already covered by combo), then bare suffixes.
55 want := []string{
56 "archive",
57 "home",
58 "home@cloud", // home@ Ă— @cloud
59 "@cloud", // bare suffix
60 }
61
62 if !reflect.DeepEqual(got, want) {
63 t.Fatalf("presets mismatch:\n got: %#v\n want: %#v", got, want)
64 }
65}
66
67func TestPresetsFromExplicitFullPreset(t *testing.T) {
68 t.Parallel()
69
70 // When an explicit full preset exists that can't be generated from
71 // prefixĂ—suffix, it should still appear.
72 const fixture = `
73[global]
74verbose = true
75
76["@cloud"]
77repo = "/repos/cloud"
78
79["special@remote"]
80repo = "/repos/special-remote"
81`
82 dir := t.TempDir()
83 path := filepath.Join(dir, "config.toml")
84 if err := os.WriteFile(path, []byte(fixture), 0o600); err != nil {
85 t.Fatalf("writing fixture: %v", err)
86 }
87
88 got := presetsFrom([]string{path})
89
90 want := []string{
91 "special@remote", // explicit full preset (no prefix@ exists)
92 "@cloud", // bare suffix
93 }
94
95 if !reflect.DeepEqual(got, want) {
96 t.Fatalf("presets mismatch:\n got: %#v\n want: %#v", got, want)
97 }
98}
99
100func TestPresetsFromMultiplePrefixesSuffixes(t *testing.T) {
101 t.Parallel()
102
103 const fixture = `
104[global]
105verbose = true
106
107["music@"]
108tag = "music"
109
110["photos@"]
111tag = "photos"
112
113["@hetzner"]
114repo = "sftp:hetzner"
115
116["@b2"]
117repo = "s3:b2"
118`
119 dir := t.TempDir()
120 path := filepath.Join(dir, "config.toml")
121 if err := os.WriteFile(path, []byte(fixture), 0o600); err != nil {
122 t.Fatalf("writing fixture: %v", err)
123 }
124
125 got := presetsFrom([]string{path})
126
127 // Combos: each prefix Ă— each suffix (prefixes sorted, suffixes sorted).
128 want := []string{
129 "music@b2",
130 "music@hetzner",
131 "photos@b2",
132 "photos@hetzner",
133 "@b2",
134 "@hetzner",
135 }
136
137 if !reflect.DeepEqual(got, want) {
138 t.Fatalf("presets mismatch:\n got: %#v\n want: %#v", got, want)
139 }
140}
141
142func TestPresetsFromEmpty(t *testing.T) {
143 t.Parallel()
144
145 dir := t.TempDir()
146 path := filepath.Join(dir, "config.toml")
147 if err := os.WriteFile(path, []byte("[global]\nverbose = true\n"), 0o600); err != nil {
148 t.Fatalf("writing fixture: %v", err)
149 }
150
151 got := presetsFrom([]string{path})
152 if len(got) != 0 {
153 t.Fatalf("expected no presets, got %#v", got)
154 }
155}
156
157func TestPresetsFromMissing(t *testing.T) {
158 t.Parallel()
159
160 got := presetsFrom([]string{"/nonexistent/path.toml"})
161 if len(got) != 0 {
162 t.Fatalf("expected nil for missing file, got %#v", got)
163 }
164}
165
166func TestPrefixesFrom(t *testing.T) {
167 t.Parallel()
168
169 dir := t.TempDir()
170 path := filepath.Join(dir, "config.toml")
171 if err := os.WriteFile(path, []byte(presetsFixtureTOML), 0o600); err != nil {
172 t.Fatalf("writing fixture: %v", err)
173 }
174
175 got := prefixesFrom([]string{path})
176 want := []string{"home@"}
177
178 if !reflect.DeepEqual(got, want) {
179 t.Fatalf("prefixes mismatch:\n got: %#v\n want: %#v", got, want)
180 }
181}
182
183func TestSuffixesFrom(t *testing.T) {
184 t.Parallel()
185
186 dir := t.TempDir()
187 path := filepath.Join(dir, "config.toml")
188 if err := os.WriteFile(path, []byte(presetsFixtureTOML), 0o600); err != nil {
189 t.Fatalf("writing fixture: %v", err)
190 }
191
192 got := suffixesFrom([]string{path})
193 want := []string{"@cloud"}
194
195 if !reflect.DeepEqual(got, want) {
196 t.Fatalf("suffixes mismatch:\n got: %#v\n want: %#v", got, want)
197 }
198}