Mercurial > go > multipass
changeset 15:9b4ec6b5c23e
Add tests for multipass files.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Thu, 29 Oct 2015 23:56:53 -0400 |
parents | 4368a377ff64 |
children | bfc035bd5132 |
files | file/file.go file/file_test.go |
diffstat | 2 files changed, 320 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/file/file.go Thu Oct 29 21:31:28 2015 -0400 +++ b/file/file.go Thu Oct 29 23:56:53 2015 -0400 @@ -23,8 +23,13 @@ ) var ( - // Raised when + // Raised when there's an error in the file format. ErrorBadFile = errors.New("multipass/file: Invalid file format") + + // we spin waiting for the file to become available, doubling our wait time + // every time it's unavailable. If the wait time is longer than this, + // give up. Variable so it can be set in tests. + maxDelay = time.Minute ) type ShadowFile struct { @@ -176,7 +181,7 @@ if !ok { return nil, err } - if errno != syscall.EEXIST || delay > time.Minute { + if errno != syscall.EEXIST || delay > maxDelay { return nil, err } time.Sleep(delay)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/file/file_test.go Thu Oct 29 23:56:53 2015 -0400 @@ -0,0 +1,313 @@ +package file + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "testing" + "time" + + "pfish.zone/go/multipass/auth" +) + +var tempdir string + +const ( + filemode = 0644 + + shadowSpooky = "123:bogushash:c3Bvb2t5" + shadowWhatever = "456:anotherhash:d2hhdGV2ZXI=" +) + +var ( + aSpooky *auth.Entry + aWhatever *auth.Entry +) + +func init() { + s, err := auth.EntryFromShadow(shadowSpooky) + if err != nil { + panic(err.Error()) + } + aSpooky = s + w, err := auth.EntryFromShadow(shadowWhatever) + if err != nil { + panic(err.Error()) + } + aWhatever = w +} + +func TestMain(m *testing.M) { + dir, err := ioutil.TempDir(os.TempDir(), "multipass-") + maxDelay = 2 * time.Second + tempdir = dir + if err != nil { + panic("couldn't create temp dir") + } + m.Run() + os.RemoveAll(tempdir) +} + +type testfile struct { + t *testing.T + dir string +} + +func mktest(t *testing.T, dir string) *testfile { + f := &testfile{t, dir} + os.Mkdir(f.dirpath(), 0700) + return f +} + +func (f *testfile) dirpath() string { + return path.Join(tempdir, f.dir) +} + +func (f *testfile) filepath() string { + return path.Join(f.dirpath(), "file") +} + +func (f *testfile) read() string { + contents, err := ioutil.ReadFile(f.filepath()) + if err != nil { + f.t.Fatalf("couldn't read output file: %q", err.Error()) + } + return string(contents) +} + +func (f *testfile) write(data string) { + err := ioutil.WriteFile(f.filepath(), []byte(data), filemode) + if err != nil { + f.t.Fatalf("couldn't write output file: %q", err) + } +} + +func (f *testfile) perms() os.FileMode { + stat, err := os.Stat(f.filepath()) + if err != nil { + f.t.Fatalf("couldn't stat output file: %q", err) + } + return stat.Mode().Perm() +} + +func (f *testfile) cleanup() { + os.RemoveAll(f.dirpath()) +} + +func TestCreate(t *testing.T) { + f := mktest(t, "create") + defer f.cleanup() + s := New(f.filepath()) + err := s.Add(aSpooky) + if err != nil { + t.Fatal(err.Error()) + } + if f.perms() != filemode { + t.Errorf("want perms %q; got %q", filemode, f.perms()) + } + want := fmt.Sprintf("%s\n%s\n", Banner, shadowSpooky) + got := f.read() + if want != got { + t.Errorf("bad file contents: want %q; got %q", want, got) + } +} + +func TestAppend(t *testing.T) { + f := mktest(t, "append") + defer f.cleanup() + f.write(fmt.Sprintf("%s\n%s\n", Banner, shadowSpooky)) + s := New(f.filepath()) + err := s.Add(aWhatever) + if err != nil { + t.Fatal(err.Error()) + } + want := fmt.Sprintf("%s\n%s\n%s\n", Banner, shadowWhatever, shadowSpooky) + got := f.read() + if want != got { + t.Errorf("bad file contents: want %q; got %q", want, got) + } +} + +func TestRemove(t *testing.T) { + f := mktest(t, "remove") + defer f.cleanup() + f.write(fmt.Sprintf("%s\n%s\n%s\n", Banner, shadowWhatever, shadowSpooky)) + s := New(f.filepath()) + err := s.Remove(456) + if err != nil { + t.Fatal(err.Error()) + } + want := fmt.Sprintf("%s\n%s\n", Banner, shadowSpooky) + got := f.read() + if want != got { + t.Errorf("bad file contents: want %q; got %q", want, got) + } + + // Removing a nonexistent entry is idempotent. + err = s.Remove(456) + if err != nil { + t.Fatal(err.Error()) + } + got = f.read() + if want != got { + t.Errorf("bad file contents: want %q; got %q", want, got) + } + + // Removing the remaining entry leaves an empty file. + err = s.Remove(123) + if err != nil { + t.Fatal(err.Error()) + } + want = fmt.Sprintf("%s\n", Banner) + got = f.read() + if want != got { + t.Errorf("bad file contents: want %q; got %q", want, got) + } +} + +func TestList(t *testing.T) { + f := mktest(t, "list") + defer f.cleanup() + s := New(f.filepath()) + entries, err := s.AllEntries() + if len(entries) != 0 { + t.Errorf("want 0 entries; got %q", entries) + } + + f.write(fmt.Sprintf("%s\n%s\n%s\n", Banner, shadowWhatever, shadowSpooky)) + entries, err = s.AllEntries() + if err != nil { + t.Fatal(err.Error()) + } + if len(entries) != 2 { + t.Fatalf("want len(entries) == 2; got entries = %q", entries) + } + if *entries[0] != *aWhatever || *entries[1] != *aSpooky { + t.Fatalf("want {aWhatever, aSpooky}; got %q", entries) + } +} + +func TestAuthenticate(t *testing.T) { + f := mktest(t, "auth") + defer f.cleanup() + s := New(f.filepath()) + eA, passA, err := auth.NewEntry("a") + if err != nil { + t.Fatalf(err.Error()) + } + eB, passB, err := auth.NewEntry("b") + if err != nil { + t.Fatalf(err.Error()) + } + err = s.Add(eA) + if err != nil { + t.Fatalf(err.Error()) + } + err = s.Add(eB) + if err != nil { + t.Fatalf(err.Error()) + } + good, err := s.Authenticate(passA) + if !good { + t.Errorf("couldn't authenticate password A") + } + good, err = s.Authenticate(passB) + if !good { + t.Errorf("couldn't authenticate password B") + } + bad, err := s.Authenticate("boo") + if bad { + t.Errorf("authenticated bad password") + } +} + +type clobberEvent int + +const ( + unlocked clobberEvent = iota + written + failed +) + +func TestNoClobber(t *testing.T) { + f := mktest(t, "clobber") + defer f.cleanup() + f.write(fmt.Sprintf("%s\n", Banner)) + s := New(f.filepath()) + // put a file in the system's new-file so it won't overwrite it. + err := ioutil.WriteFile(s.newFilename(), nil, filemode) + if err != nil { + t.Fatalf(err.Error()) + } + events := make(chan clobberEvent, 10) + go func() { + err := s.Add(aWhatever) + if err != nil { + events <- failed + return + } + events <- written + }() + time.Sleep(time.Second) + events <- unlocked + err = os.Remove(s.newFilename()) + if err != nil { + t.Fatalf(err.Error()) + } + ev := <-events + if unlocked != ev { + t.Errorf("event 0: %q; got %q", unlocked, ev) + } + ev = <-events + if written != ev { + t.Errorf("event 1: %q; got %q", written, ev) + } + + want := fmt.Sprintf("%s\n%s\n", Banner, shadowWhatever) + got := f.read() + if want != got { + t.Errorf("bad contents: want %q; got %q", want, got) + } +} + +func TestGiveUp(t *testing.T) { + f := mktest(t, "giveup") + defer f.cleanup() + f.write(fmt.Sprintf("%s\n", Banner)) + s := New(f.filepath()) + // put a file in the system's new-file so it won't overwrite it. + err := ioutil.WriteFile(s.newFilename(), nil, filemode) + if err != nil { + t.Fatalf(err.Error()) + } + events := make(chan clobberEvent, 10) + go func() { + err := s.Add(aWhatever) + if err != nil { + events <- failed + return + } + events <- written + }() + time.Sleep(5 * time.Second) + events <- unlocked + err = os.Remove(s.newFilename()) + if err != nil { + t.Fatalf(err.Error()) + } + ev := <-events + if failed != ev { + t.Errorf("event 0: want %q; got %q", failed, ev) + } + ev = <-events + if unlocked != ev { + t.Errorf("event 1: want %q; got %q", unlocked, ev) + } + + want := fmt.Sprintf("%s\n", Banner) + got := f.read() + if want != got { + t.Errorf("bad contents: want %q; got %q", want, got) + } +}