1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-17 17:47:03 +00:00

Large refactor for #69

- makefile: Don't refresh deps automatically, causes double build
- storage: Move GetMessage, GetMessages (Mailbox), PurgeMessages to the
  Store API for #69
- storage: Remove Mailbox.Name method for #69
- test: Create new test package for #79
- test: Implement StoreStub, migrate some tests off MockDataStore for
  task #80
- rest & webui: update controllers to use new Store methods
This commit is contained in:
James Hillyerd
2018-03-10 18:50:18 -08:00
parent a58dfc5e4f
commit 9c18f1fb30
12 changed files with 160 additions and 227 deletions

View File

@@ -81,9 +81,36 @@ func DefaultStore() storage.Store {
return New(cfg)
}
// GetMessage returns the messages in the named mailbox, or an error.
func (fs *Store) GetMessage(mailbox, id string) (storage.Message, error) {
mb, err := fs.MailboxFor(mailbox)
if err != nil {
return nil, err
}
return mb.(*Mailbox).GetMessage(id)
}
// GetMessages returns the messages in the named mailbox, or an error.
func (fs *Store) GetMessages(mailbox string) ([]storage.Message, error) {
mb, err := fs.MailboxFor(mailbox)
if err != nil {
return nil, err
}
return mb.(*Mailbox).GetMessages()
}
// PurgeMessages deletes all messages in the named mailbox, or returns an error.
func (fs *Store) PurgeMessages(name string) error {
mb, err := fs.MailboxFor(name)
if err != nil {
return err
}
return mb.(*Mailbox).Purge()
}
// MailboxFor retrieves the Mailbox object for a specified email address, if the mailbox
// does not exist, it will attempt to create it.
func (ds *Store) MailboxFor(emailAddress string) (storage.Mailbox, error) {
func (fs *Store) MailboxFor(emailAddress string) (storage.Mailbox, error) {
name, err := stringutil.ParseMailboxName(emailAddress)
if err != nil {
return nil, err
@@ -91,17 +118,17 @@ func (ds *Store) MailboxFor(emailAddress string) (storage.Mailbox, error) {
dir := stringutil.HashMailboxName(name)
s1 := dir[0:3]
s2 := dir[0:6]
path := filepath.Join(ds.mailPath, s1, s2, dir)
path := filepath.Join(fs.mailPath, s1, s2, dir)
indexPath := filepath.Join(path, indexFileName)
return &Mailbox{store: ds, name: name, dirName: dir, path: path,
return &Mailbox{store: fs, name: name, dirName: dir, path: path,
indexPath: indexPath}, nil
}
// AllMailboxes returns a slice with all Mailboxes
func (ds *Store) AllMailboxes() ([]storage.Mailbox, error) {
func (fs *Store) AllMailboxes() ([]storage.Mailbox, error) {
mailboxes := make([]storage.Mailbox, 0, 100)
infos1, err := ioutil.ReadDir(ds.mailPath)
infos1, err := ioutil.ReadDir(fs.mailPath)
if err != nil {
return nil, err
}
@@ -109,7 +136,7 @@ func (ds *Store) AllMailboxes() ([]storage.Mailbox, error) {
for _, inf1 := range infos1 {
if inf1.IsDir() {
l1 := inf1.Name()
infos2, err := ioutil.ReadDir(filepath.Join(ds.mailPath, l1))
infos2, err := ioutil.ReadDir(filepath.Join(fs.mailPath, l1))
if err != nil {
return nil, err
}
@@ -117,7 +144,7 @@ func (ds *Store) AllMailboxes() ([]storage.Mailbox, error) {
for _, inf2 := range infos2 {
if inf2.IsDir() {
l2 := inf2.Name()
infos3, err := ioutil.ReadDir(filepath.Join(ds.mailPath, l1, l2))
infos3, err := ioutil.ReadDir(filepath.Join(fs.mailPath, l1, l2))
if err != nil {
return nil, err
}
@@ -125,9 +152,9 @@ func (ds *Store) AllMailboxes() ([]storage.Mailbox, error) {
for _, inf3 := range infos3 {
if inf3.IsDir() {
mbdir := inf3.Name()
mbpath := filepath.Join(ds.mailPath, l1, l2, mbdir)
mbpath := filepath.Join(fs.mailPath, l1, l2, mbdir)
idx := filepath.Join(mbpath, indexFileName)
mb := &Mailbox{store: ds, dirName: mbdir, path: mbpath,
mb := &Mailbox{store: fs, dirName: mbdir, path: mbpath,
indexPath: idx}
mailboxes = append(mailboxes, mb)
}
@@ -141,13 +168,13 @@ func (ds *Store) AllMailboxes() ([]storage.Mailbox, error) {
}
// LockFor returns the RWMutex for this mailbox, or an error.
func (ds *Store) LockFor(emailAddress string) (*sync.RWMutex, error) {
func (fs *Store) LockFor(emailAddress string) (*sync.RWMutex, error) {
name, err := stringutil.ParseMailboxName(emailAddress)
if err != nil {
return nil, err
}
hash := stringutil.HashMailboxName(name)
return ds.hashLock.Get(hash), nil
return fs.hashLock.Get(hash), nil
}
// Mailbox implements Mailbox, manages the mail for a specific user and

View File

@@ -62,9 +62,7 @@ func TestFSDirStructure(t *testing.T) {
assert.True(t, isFile(expect), "Expected %q to be a file", expect)
// Delete message
mb, err := ds.MailboxFor(mbName)
assert.Nil(t, err)
msg, err := mb.GetMessage(id1)
msg, err := ds.GetMessage(mbName, id1)
assert.Nil(t, err)
err = msg.Delete()
assert.Nil(t, err)
@@ -76,7 +74,7 @@ func TestFSDirStructure(t *testing.T) {
assert.True(t, isFile(expect), "Expected %q to be a file", expect)
// Delete message
msg, err = mb.GetMessage(id2)
msg, err = ds.GetMessage(mbName, id2)
assert.Nil(t, err)
err = msg.Delete()
assert.Nil(t, err)
@@ -137,11 +135,7 @@ func TestFSDeliverMany(t *testing.T) {
for i, subj := range subjects {
// Check number of messages
mb, err := ds.MailboxFor(mbName)
if err != nil {
t.Fatalf("Failed to MailboxFor(%q): %v", mbName, err)
}
msgs, err := mb.GetMessages()
msgs, err := ds.GetMessages(mbName)
if err != nil {
t.Fatalf("Failed to GetMessages for %q: %v", mbName, err)
}
@@ -151,11 +145,7 @@ func TestFSDeliverMany(t *testing.T) {
deliverMessage(ds, mbName, subj, time.Now())
}
mb, err := ds.MailboxFor(mbName)
if err != nil {
t.Fatalf("Failed to MailboxFor(%q): %v", mbName, err)
}
msgs, err := mb.GetMessages()
msgs, err := ds.GetMessages(mbName)
if err != nil {
t.Fatalf("Failed to GetMessages for %q: %v", mbName, err)
}
@@ -189,11 +179,7 @@ func TestFSDelete(t *testing.T) {
deliverMessage(ds, mbName, subj, time.Now())
}
mb, err := ds.MailboxFor(mbName)
if err != nil {
t.Fatalf("Failed to MailboxFor(%q): %v", mbName, err)
}
msgs, err := mb.GetMessages()
msgs, err := ds.GetMessages(mbName)
if err != nil {
t.Fatalf("Failed to GetMessages for %q: %v", mbName, err)
}
@@ -205,11 +191,7 @@ func TestFSDelete(t *testing.T) {
_ = msgs[3].Delete()
// Confirm deletion
mb, err = ds.MailboxFor(mbName)
if err != nil {
t.Fatalf("Failed to MailboxFor(%q): %v", mbName, err)
}
msgs, err = mb.GetMessages()
msgs, err = ds.GetMessages(mbName)
if err != nil {
t.Fatalf("Failed to GetMessages for %q: %v", mbName, err)
}
@@ -225,11 +207,7 @@ func TestFSDelete(t *testing.T) {
// Try appending one more
deliverMessage(ds, mbName, "foxtrot", time.Now())
mb, err = ds.MailboxFor(mbName)
if err != nil {
t.Fatalf("Failed to MailboxFor(%q): %v", mbName, err)
}
msgs, err = mb.GetMessages()
msgs, err = ds.GetMessages(mbName)
if err != nil {
t.Fatalf("Failed to GetMessages for %q: %v", mbName, err)
}
@@ -263,11 +241,7 @@ func TestFSPurge(t *testing.T) {
deliverMessage(ds, mbName, subj, time.Now())
}
mb, err := ds.MailboxFor(mbName)
if err != nil {
t.Fatalf("Failed to MailboxFor(%q): %v", mbName, err)
}
msgs, err := mb.GetMessages()
msgs, err := ds.GetMessages(mbName)
if err != nil {
t.Fatalf("Failed to GetMessages for %q: %v", mbName, err)
}
@@ -275,15 +249,11 @@ func TestFSPurge(t *testing.T) {
len(subjects), len(msgs))
// Purge mailbox
err = mb.Purge()
err = ds.PurgeMessages(mbName)
assert.Nil(t, err)
// Confirm deletion
mb, err = ds.MailboxFor(mbName)
if err != nil {
t.Fatalf("Failed to MailboxFor(%q): %v", mbName, err)
}
msgs, err = mb.GetMessages()
msgs, err = ds.GetMessages(mbName)
if err != nil {
t.Fatalf("Failed to GetMessages for %q: %v", mbName, err)
}
@@ -315,12 +285,8 @@ func TestFSSize(t *testing.T) {
sentSizes[i] = size
}
mb, err := ds.MailboxFor(mbName)
if err != nil {
t.Fatalf("Failed to MailboxFor(%q): %v", mbName, err)
}
for i, id := range sentIds {
msg, err := mb.GetMessage(id)
msg, err := ds.GetMessage(mbName, id)
assert.Nil(t, err)
expect := sentSizes[i]
@@ -351,17 +317,12 @@ func TestFSMissing(t *testing.T) {
sentIds[i] = id
}
mb, err := ds.MailboxFor(mbName)
if err != nil {
t.Fatalf("Failed to MailboxFor(%q): %v", mbName, err)
}
// Delete a message file without removing it from index
msg, err := mb.GetMessage(sentIds[1])
msg, err := ds.GetMessage(mbName, sentIds[1])
assert.Nil(t, err)
fmsg := msg.(*Message)
_ = os.Remove(fmsg.rawPath())
msg, err = mb.GetMessage(sentIds[1])
msg, err = ds.GetMessage(mbName, sentIds[1])
assert.Nil(t, err)
// Try to read parts of message
@@ -392,11 +353,7 @@ func TestFSMessageCap(t *testing.T) {
t.Logf("Delivered %q", subj)
// Check number of messages
mb, err := ds.MailboxFor(mbName)
if err != nil {
t.Fatalf("Failed to MailboxFor(%q): %v", mbName, err)
}
msgs, err := mb.GetMessages()
msgs, err := ds.GetMessages(mbName)
if err != nil {
t.Fatalf("Failed to GetMessages for %q: %v", mbName, err)
}
@@ -437,11 +394,7 @@ func TestFSNoMessageCap(t *testing.T) {
t.Logf("Delivered %q", subj)
// Check number of messages
mb, err := ds.MailboxFor(mbName)
if err != nil {
t.Fatalf("Failed to MailboxFor(%q): %v", mbName, err)
}
msgs, err := mb.GetMessages()
msgs, err := ds.GetMessages(mbName)
if err != nil {
t.Fatalf("Failed to GetMessages for %q: %v", mbName, err)
}
@@ -467,9 +420,7 @@ func TestGetLatestMessage(t *testing.T) {
mbName := "james"
// Test empty mailbox
mb, err := ds.MailboxFor(mbName)
assert.Nil(t, err)
msg, err := mb.GetMessage("latest")
msg, err := ds.GetMessage(mbName, "latest")
assert.Nil(t, msg)
assert.Error(t, err)
@@ -480,23 +431,19 @@ func TestGetLatestMessage(t *testing.T) {
id2, _ := deliverMessage(ds, mbName, "test 2", time.Now())
// Test get the latest message
mb, err = ds.MailboxFor(mbName)
assert.Nil(t, err)
msg, err = mb.GetMessage("latest")
msg, err = ds.GetMessage(mbName, "latest")
assert.Nil(t, err)
assert.True(t, msg.ID() == id2, "Expected %q to be equal to %q", msg.ID(), id2)
// Deliver test message 3
id3, _ := deliverMessage(ds, mbName, "test 3", time.Now())
mb, err = ds.MailboxFor(mbName)
assert.Nil(t, err)
msg, err = mb.GetMessage("latest")
msg, err = ds.GetMessage(mbName, "latest")
assert.Nil(t, err)
assert.True(t, msg.ID() == id3, "Expected %q to be equal to %q", msg.ID(), id3)
// Test wrong id
_, err = mb.GetMessage("wrongid")
_, err = ds.GetMessage(mbName, "wrongid")
assert.Error(t, err)
if t.Failed() {

View File

@@ -21,6 +21,9 @@ var (
// Store is an interface to get Mailboxes stored in Inbucket
type Store interface {
GetMessage(mailbox string, id string) (Message, error)
GetMessages(mailbox string) ([]Message, error)
PurgeMessages(mailbox string) error
MailboxFor(emailAddress string) (Mailbox, error)
AllMailboxes() ([]Mailbox, error)
// LockFor is a temporary hack to fix #77 until Datastore revamp
@@ -30,10 +33,7 @@ type Store interface {
// Mailbox is an interface to get and manipulate messages in a DataStore
type Mailbox interface {
GetMessages() ([]Message, error)
GetMessage(id string) (Message, error)
Purge() error
NewMessage() (Message, error)
Name() string
String() string
}

View File

@@ -15,6 +15,24 @@ type MockDataStore struct {
mock.Mock
}
// GetMessage mock function
func (m *MockDataStore) GetMessage(name, id string) (Message, error) {
args := m.Called(name, id)
return args.Get(0).(Message), args.Error(1)
}
// GetMessages mock function
func (m *MockDataStore) GetMessages(name string) ([]Message, error) {
args := m.Called(name)
return args.Get(0).([]Message), args.Error(1)
}
// PurgeMessages mock function
func (m *MockDataStore) PurgeMessages(name string) error {
args := m.Called(name)
return args.Error(0)
}
// MailboxFor mock function
func (m *MockDataStore) MailboxFor(name string) (Mailbox, error) {
args := m.Called(name)
@@ -61,12 +79,6 @@ func (m *MockMailbox) NewMessage() (Message, error) {
return args.Get(0).(Message), args.Error(1)
}
// Name mock function
func (m *MockMailbox) Name() string {
args := m.Called()
return args.String(0)
}
// String mock function
func (m *MockMailbox) String() string {
args := m.Called()