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 }
|