view file/file_test.go @ 15:9b4ec6b5c23e

Add tests for multipass files.
author Paul Fisher <paul@pfish.zone>
date Thu, 29 Oct 2015 23:56:53 -0400
parents
children bfc035bd5132
line wrap: on
line source

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