15
|
1 package file |
|
2 |
|
3 import ( |
|
4 "fmt" |
|
5 "io/ioutil" |
|
6 "os" |
|
7 "path" |
|
8 "testing" |
|
9 "time" |
|
10 |
|
11 "pfish.zone/go/multipass/auth" |
|
12 ) |
|
13 |
|
14 var tempdir string |
|
15 |
|
16 const ( |
|
17 filemode = 0644 |
|
18 |
|
19 shadowSpooky = "123:bogushash:c3Bvb2t5" |
|
20 shadowWhatever = "456:anotherhash:d2hhdGV2ZXI=" |
|
21 ) |
|
22 |
|
23 var ( |
|
24 aSpooky *auth.Entry |
|
25 aWhatever *auth.Entry |
|
26 ) |
|
27 |
|
28 func init() { |
|
29 s, err := auth.EntryFromShadow(shadowSpooky) |
|
30 if err != nil { |
|
31 panic(err.Error()) |
|
32 } |
|
33 aSpooky = s |
|
34 w, err := auth.EntryFromShadow(shadowWhatever) |
|
35 if err != nil { |
|
36 panic(err.Error()) |
|
37 } |
|
38 aWhatever = w |
|
39 } |
|
40 |
|
41 func TestMain(m *testing.M) { |
|
42 dir, err := ioutil.TempDir(os.TempDir(), "multipass-") |
|
43 maxDelay = 2 * time.Second |
|
44 tempdir = dir |
|
45 if err != nil { |
|
46 panic("couldn't create temp dir") |
|
47 } |
|
48 m.Run() |
|
49 os.RemoveAll(tempdir) |
|
50 } |
|
51 |
|
52 type testfile struct { |
|
53 t *testing.T |
|
54 dir string |
|
55 } |
|
56 |
|
57 func mktest(t *testing.T, dir string) *testfile { |
|
58 f := &testfile{t, dir} |
|
59 os.Mkdir(f.dirpath(), 0700) |
|
60 return f |
|
61 } |
|
62 |
|
63 func (f *testfile) dirpath() string { |
|
64 return path.Join(tempdir, f.dir) |
|
65 } |
|
66 |
|
67 func (f *testfile) filepath() string { |
|
68 return path.Join(f.dirpath(), "file") |
|
69 } |
|
70 |
|
71 func (f *testfile) read() string { |
|
72 contents, err := ioutil.ReadFile(f.filepath()) |
|
73 if err != nil { |
|
74 f.t.Fatalf("couldn't read output file: %q", err.Error()) |
|
75 } |
|
76 return string(contents) |
|
77 } |
|
78 |
|
79 func (f *testfile) write(data string) { |
|
80 err := ioutil.WriteFile(f.filepath(), []byte(data), filemode) |
|
81 if err != nil { |
|
82 f.t.Fatalf("couldn't write output file: %q", err) |
|
83 } |
|
84 } |
|
85 |
|
86 func (f *testfile) perms() os.FileMode { |
|
87 stat, err := os.Stat(f.filepath()) |
|
88 if err != nil { |
|
89 f.t.Fatalf("couldn't stat output file: %q", err) |
|
90 } |
|
91 return stat.Mode().Perm() |
|
92 } |
|
93 |
|
94 func (f *testfile) cleanup() { |
|
95 os.RemoveAll(f.dirpath()) |
|
96 } |
|
97 |
|
98 func TestCreate(t *testing.T) { |
|
99 f := mktest(t, "create") |
|
100 defer f.cleanup() |
|
101 s := New(f.filepath()) |
|
102 err := s.Add(aSpooky) |
|
103 if err != nil { |
|
104 t.Fatal(err.Error()) |
|
105 } |
|
106 if f.perms() != filemode { |
|
107 t.Errorf("want perms %q; got %q", filemode, f.perms()) |
|
108 } |
|
109 want := fmt.Sprintf("%s\n%s\n", Banner, shadowSpooky) |
|
110 got := f.read() |
|
111 if want != got { |
|
112 t.Errorf("bad file contents: want %q; got %q", want, got) |
|
113 } |
|
114 } |
|
115 |
|
116 func TestAppend(t *testing.T) { |
|
117 f := mktest(t, "append") |
|
118 defer f.cleanup() |
|
119 f.write(fmt.Sprintf("%s\n%s\n", Banner, shadowSpooky)) |
|
120 s := New(f.filepath()) |
|
121 err := s.Add(aWhatever) |
|
122 if err != nil { |
|
123 t.Fatal(err.Error()) |
|
124 } |
|
125 want := fmt.Sprintf("%s\n%s\n%s\n", Banner, shadowWhatever, shadowSpooky) |
|
126 got := f.read() |
|
127 if want != got { |
|
128 t.Errorf("bad file contents: want %q; got %q", want, got) |
|
129 } |
|
130 } |
|
131 |
|
132 func TestRemove(t *testing.T) { |
|
133 f := mktest(t, "remove") |
|
134 defer f.cleanup() |
|
135 f.write(fmt.Sprintf("%s\n%s\n%s\n", Banner, shadowWhatever, shadowSpooky)) |
|
136 s := New(f.filepath()) |
|
137 err := s.Remove(456) |
|
138 if err != nil { |
|
139 t.Fatal(err.Error()) |
|
140 } |
|
141 want := fmt.Sprintf("%s\n%s\n", Banner, shadowSpooky) |
|
142 got := f.read() |
|
143 if want != got { |
|
144 t.Errorf("bad file contents: want %q; got %q", want, got) |
|
145 } |
|
146 |
|
147 // Removing a nonexistent entry is idempotent. |
|
148 err = s.Remove(456) |
|
149 if err != nil { |
|
150 t.Fatal(err.Error()) |
|
151 } |
|
152 got = f.read() |
|
153 if want != got { |
|
154 t.Errorf("bad file contents: want %q; got %q", want, got) |
|
155 } |
|
156 |
|
157 // Removing the remaining entry leaves an empty file. |
|
158 err = s.Remove(123) |
|
159 if err != nil { |
|
160 t.Fatal(err.Error()) |
|
161 } |
|
162 want = fmt.Sprintf("%s\n", Banner) |
|
163 got = f.read() |
|
164 if want != got { |
|
165 t.Errorf("bad file contents: want %q; got %q", want, got) |
|
166 } |
|
167 } |
|
168 |
|
169 func TestList(t *testing.T) { |
|
170 f := mktest(t, "list") |
|
171 defer f.cleanup() |
|
172 s := New(f.filepath()) |
|
173 entries, err := s.AllEntries() |
|
174 if len(entries) != 0 { |
|
175 t.Errorf("want 0 entries; got %q", entries) |
|
176 } |
|
177 |
|
178 f.write(fmt.Sprintf("%s\n%s\n%s\n", Banner, shadowWhatever, shadowSpooky)) |
|
179 entries, err = s.AllEntries() |
|
180 if err != nil { |
|
181 t.Fatal(err.Error()) |
|
182 } |
|
183 if len(entries) != 2 { |
|
184 t.Fatalf("want len(entries) == 2; got entries = %q", entries) |
|
185 } |
|
186 if *entries[0] != *aWhatever || *entries[1] != *aSpooky { |
|
187 t.Fatalf("want {aWhatever, aSpooky}; got %q", entries) |
|
188 } |
|
189 } |
|
190 |
|
191 func TestAuthenticate(t *testing.T) { |
|
192 f := mktest(t, "auth") |
|
193 defer f.cleanup() |
|
194 s := New(f.filepath()) |
|
195 eA, passA, err := auth.NewEntry("a") |
|
196 if err != nil { |
|
197 t.Fatalf(err.Error()) |
|
198 } |
|
199 eB, passB, err := auth.NewEntry("b") |
|
200 if err != nil { |
|
201 t.Fatalf(err.Error()) |
|
202 } |
|
203 err = s.Add(eA) |
|
204 if err != nil { |
|
205 t.Fatalf(err.Error()) |
|
206 } |
|
207 err = s.Add(eB) |
|
208 if err != nil { |
|
209 t.Fatalf(err.Error()) |
|
210 } |
|
211 good, err := s.Authenticate(passA) |
|
212 if !good { |
|
213 t.Errorf("couldn't authenticate password A") |
|
214 } |
|
215 good, err = s.Authenticate(passB) |
|
216 if !good { |
|
217 t.Errorf("couldn't authenticate password B") |
|
218 } |
|
219 bad, err := s.Authenticate("boo") |
|
220 if bad { |
|
221 t.Errorf("authenticated bad password") |
|
222 } |
|
223 } |
|
224 |
|
225 type clobberEvent int |
|
226 |
|
227 const ( |
|
228 unlocked clobberEvent = iota |
|
229 written |
|
230 failed |
|
231 ) |
|
232 |
|
233 func TestNoClobber(t *testing.T) { |
|
234 f := mktest(t, "clobber") |
|
235 defer f.cleanup() |
|
236 f.write(fmt.Sprintf("%s\n", Banner)) |
|
237 s := New(f.filepath()) |
|
238 // put a file in the system's new-file so it won't overwrite it. |
|
239 err := ioutil.WriteFile(s.newFilename(), nil, filemode) |
|
240 if err != nil { |
|
241 t.Fatalf(err.Error()) |
|
242 } |
|
243 events := make(chan clobberEvent, 10) |
|
244 go func() { |
|
245 err := s.Add(aWhatever) |
|
246 if err != nil { |
|
247 events <- failed |
|
248 return |
|
249 } |
|
250 events <- written |
|
251 }() |
|
252 time.Sleep(time.Second) |
|
253 events <- unlocked |
|
254 err = os.Remove(s.newFilename()) |
|
255 if err != nil { |
|
256 t.Fatalf(err.Error()) |
|
257 } |
|
258 ev := <-events |
|
259 if unlocked != ev { |
|
260 t.Errorf("event 0: %q; got %q", unlocked, ev) |
|
261 } |
|
262 ev = <-events |
|
263 if written != ev { |
|
264 t.Errorf("event 1: %q; got %q", written, ev) |
|
265 } |
|
266 |
|
267 want := fmt.Sprintf("%s\n%s\n", Banner, shadowWhatever) |
|
268 got := f.read() |
|
269 if want != got { |
|
270 t.Errorf("bad contents: want %q; got %q", want, got) |
|
271 } |
|
272 } |
|
273 |
|
274 func TestGiveUp(t *testing.T) { |
|
275 f := mktest(t, "giveup") |
|
276 defer f.cleanup() |
|
277 f.write(fmt.Sprintf("%s\n", Banner)) |
|
278 s := New(f.filepath()) |
|
279 // put a file in the system's new-file so it won't overwrite it. |
|
280 err := ioutil.WriteFile(s.newFilename(), nil, filemode) |
|
281 if err != nil { |
|
282 t.Fatalf(err.Error()) |
|
283 } |
|
284 events := make(chan clobberEvent, 10) |
|
285 go func() { |
|
286 err := s.Add(aWhatever) |
|
287 if err != nil { |
|
288 events <- failed |
|
289 return |
|
290 } |
|
291 events <- written |
|
292 }() |
|
293 time.Sleep(5 * time.Second) |
|
294 events <- unlocked |
|
295 err = os.Remove(s.newFilename()) |
|
296 if err != nil { |
|
297 t.Fatalf(err.Error()) |
|
298 } |
|
299 ev := <-events |
|
300 if failed != ev { |
|
301 t.Errorf("event 0: want %q; got %q", failed, ev) |
|
302 } |
|
303 ev = <-events |
|
304 if unlocked != ev { |
|
305 t.Errorf("event 1: want %q; got %q", unlocked, ev) |
|
306 } |
|
307 |
|
308 want := fmt.Sprintf("%s\n", Banner) |
|
309 got := f.read() |
|
310 if want != got { |
|
311 t.Errorf("bad contents: want %q; got %q", want, got) |
|
312 } |
|
313 } |