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

storage_suite: refactor to use common struct param (#492)

Signed-off-by: James Hillyerd <james@hillyerd.com>
This commit is contained in:
James Hillyerd
2024-02-16 16:11:07 -08:00
committed by GitHub
parent 0361e971e0
commit f76b93a8f2
3 changed files with 181 additions and 147 deletions

View File

@@ -82,6 +82,7 @@ func (s *Store) AddMessage(message storage.Message) (id string, err error) {
m.id = id m.id = id
m.source = source m.source = source
mb.messages[id] = m mb.messages[id] = m
if s.cap > 0 { if s.cap > 0 {
// Enforce cap. // Enforce cap.
for len(mb.messages) > s.cap { for len(mb.messages) > s.cap {

View File

@@ -0,0 +1,61 @@
package test
import (
"fmt"
"io"
"net/mail"
"strings"
"testing"
"time"
"github.com/inbucket/inbucket/v3/pkg/extension/event"
"github.com/inbucket/inbucket/v3/pkg/message"
"github.com/inbucket/inbucket/v3/pkg/storage"
)
// DeliverToStore creates and delivers a message to the specific mailbox, returning the size of the
// generated message.
func DeliverToStore(
t *testing.T,
store storage.Store,
mailbox string,
subject string,
date time.Time,
) (string, int64) {
t.Helper()
meta := event.MessageMetadata{
Mailbox: mailbox,
To: []*mail.Address{{Name: "Some Body", Address: "somebody@host"}},
From: &mail.Address{Name: "Some B. Else", Address: "somebodyelse@host"},
Subject: subject,
Date: date,
}
testMsg := fmt.Sprintf("To: %s\r\nFrom: %s\r\nSubject: %s\r\n\r\nTest Body\r\n",
meta.To[0].Address, meta.From.Address, subject)
delivery := &message.Delivery{
Meta: meta,
Reader: io.NopCloser(strings.NewReader(testMsg)),
}
id, err := store.AddMessage(delivery)
if err != nil {
t.Fatal(err)
}
return id, int64(len(testMsg))
}
// GetAndCountMessages is a test helper that expects to receive count messages or fails the test, it
// also checks return error.
func GetAndCountMessages(t *testing.T, s storage.Store, mailbox string, count int) []storage.Message {
t.Helper()
msgs, err := s.GetMessages(mailbox)
if err != nil {
t.Fatalf("Failed to GetMessages for %q: %v", mailbox, err)
}
if len(msgs) != count {
t.Errorf("Got %v messages for %q, want: %v", len(msgs), mailbox, count)
}
return msgs
}

View File

@@ -22,11 +22,19 @@ import (
type StoreFactory func( type StoreFactory func(
config.Storage, *extension.Host) (store storage.Store, destroy func(), err error) config.Storage, *extension.Host) (store storage.Store, destroy func(), err error)
// suite is passed to each test function; embeds `testing.T` to provide testing primitives.
type suite struct {
*testing.T
store storage.Store
extHost *extension.Host
}
// StoreSuite runs a set of general tests on the provided Store. // StoreSuite runs a set of general tests on the provided Store.
func StoreSuite(t *testing.T, factory StoreFactory) { func StoreSuite(t *testing.T, factory StoreFactory) {
t.Helper()
testCases := []struct { testCases := []struct {
name string name string
test func(*testing.T, storage.Store, *extension.Host) test func(suite)
conf config.Storage conf config.Storage
}{ }{
{"metadata", testMetadata, config.Storage{}}, {"metadata", testMetadata, config.Storage{}},
@@ -49,14 +57,20 @@ func StoreSuite(t *testing.T, factory StoreFactory) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
tc.test(t, store, extHost) defer destroy()
destroy()
s := suite{
T: t,
store: store,
extHost: extHost,
}
tc.test(s)
}) })
} }
} }
// testMetadata verifies message metadata is stored and retrieved correctly. // testMetadata verifies message metadata is stored and retrieved correctly.
func testMetadata(t *testing.T, store storage.Store, extHost *extension.Host) { func testMetadata(s suite) {
mailbox := "testmailbox" mailbox := "testmailbox"
from := &mail.Address{Name: "From Person", Address: "from@person.com"} from := &mail.Address{Name: "From Person", Address: "from@person.com"}
to := []*mail.Address{ to := []*mail.Address{
@@ -78,52 +92,52 @@ func testMetadata(t *testing.T, store storage.Store, extHost *extension.Host) {
}, },
Reader: strings.NewReader(content), Reader: strings.NewReader(content),
} }
id, err := store.AddMessage(delivery) id, err := s.store.AddMessage(delivery)
if err != nil { if err != nil {
t.Fatal(err) s.Fatal(err)
} }
if id == "" { if id == "" {
t.Fatal("Expected AddMessage() to return non-empty ID string") s.Fatal("Expected AddMessage() to return non-empty ID string")
} }
// Retrieve and validate the message. // Retrieve and validate the message.
sm, err := store.GetMessage(mailbox, id) sm, err := s.store.GetMessage(mailbox, id)
if err != nil { if err != nil {
t.Fatal(err) s.Fatal(err)
} }
if sm.Mailbox() != mailbox { if sm.Mailbox() != mailbox {
t.Errorf("got mailbox %q, want: %q", sm.Mailbox(), mailbox) s.Errorf("got mailbox %q, want: %q", sm.Mailbox(), mailbox)
} }
if sm.ID() != id { if sm.ID() != id {
t.Errorf("got id %q, want: %q", sm.ID(), id) s.Errorf("got id %q, want: %q", sm.ID(), id)
} }
if *sm.From() != *from { if *sm.From() != *from {
t.Errorf("got from %v, want: %v", sm.From(), from) s.Errorf("got from %v, want: %v", sm.From(), from)
} }
if len(sm.To()) != len(to) { if len(sm.To()) != len(to) {
t.Errorf("got len(to) = %v, want: %v", len(sm.To()), len(to)) s.Errorf("got len(to) = %v, want: %v", len(sm.To()), len(to))
} else { } else {
for i, got := range sm.To() { for i, got := range sm.To() {
if *to[i] != *got { if *to[i] != *got {
t.Errorf("got to[%v] %v, want: %v", i, got, to[i]) s.Errorf("got to[%v] %v, want: %v", i, got, to[i])
} }
} }
} }
if !sm.Date().Equal(date) { if !sm.Date().Equal(date) {
t.Errorf("got date %v, want: %v", sm.Date(), date) s.Errorf("got date %v, want: %v", sm.Date(), date)
} }
if sm.Subject() != subject { if sm.Subject() != subject {
t.Errorf("got subject %q, want: %q", sm.Subject(), subject) s.Errorf("got subject %q, want: %q", sm.Subject(), subject)
} }
if sm.Size() != int64(len(content)) { if sm.Size() != int64(len(content)) {
t.Errorf("got size %v, want: %v", sm.Size(), len(content)) s.Errorf("got size %v, want: %v", sm.Size(), len(content))
} }
if sm.Seen() { if sm.Seen() {
t.Errorf("got seen %v, want: false", sm.Seen()) s.Errorf("got seen %v, want: false", sm.Seen())
} }
} }
// testContent generates some binary content and makes sure it is correctly retrieved. // testContent generates some binary content and makes sure it is correctly retrieved.
func testContent(t *testing.T, store storage.Store, extHost *extension.Host) { func testContent(s suite) {
content := make([]byte, 5000) content := make([]byte, 5000)
for i := 0; i < len(content); i++ { for i := 0; i < len(content); i++ {
content[i] = byte(i % 256) content[i] = byte(i % 256)
@@ -146,332 +160,290 @@ func testContent(t *testing.T, store storage.Store, extHost *extension.Host) {
}, },
Reader: bytes.NewReader(content), Reader: bytes.NewReader(content),
} }
id, err := store.AddMessage(delivery) id, err := s.store.AddMessage(delivery)
require.NoError(t, err, "AddMessage() failed") require.NoError(s, err, "AddMessage() failed")
// Read stored message source. // Read stored message source.
m, err := store.GetMessage(mailbox, id) m, err := s.store.GetMessage(mailbox, id)
require.NoError(t, err, "GetMessage() failed") require.NoError(s, err, "GetMessage() failed")
r, err := m.Source() r, err := m.Source()
require.NoError(t, err, "Source() failed") require.NoError(s, err, "Source() failed")
got, err := io.ReadAll(r) got, err := io.ReadAll(r)
require.NoError(t, err, "failed to read source") require.NoError(s, err, "failed to read source")
err = r.Close() err = r.Close()
require.NoError(t, err, "failed to close source reader") require.NoError(s, err, "failed to close source reader")
// Verify source. // Verify source.
if len(got) != len(content) { if len(got) != len(content) {
t.Errorf("Got len(content) == %v, want: %v", len(got), len(content)) s.Errorf("Got len(content) == %v, want: %v", len(got), len(content))
} }
errors := 0 errors := 0
for i, b := range got { for i, b := range got {
if b != content[i] { if b != content[i] {
t.Errorf("Got content[%v] == %v, want: %v", i, b, content[i]) s.Errorf("Got content[%v] == %v, want: %v", i, b, content[i])
errors++ errors++
} }
if errors > 5 { if errors > 5 {
t.Fatalf("Too many content errors, aborting test.") s.Fatalf("Too many content errors, aborting test.")
} }
} }
} }
// testDeliveryOrder delivers several messages to the same mailbox, meanwhile querying its contents // testDeliveryOrder delivers several messages to the same mailbox, meanwhile querying its contents
// with a new GetMessages call each cycle. // with a new GetMessages call each cycle.
func testDeliveryOrder(t *testing.T, store storage.Store, extHost *extension.Host) { func testDeliveryOrder(s suite) {
mailbox := "fred" mailbox := "fred"
subjects := []string{"alpha", "bravo", "charlie", "delta", "echo"} subjects := []string{"alpha", "bravo", "charlie", "delta", "echo"}
for i, subj := range subjects { for i, subj := range subjects {
// Check mailbox count. // Check mailbox count.
GetAndCountMessages(t, store, mailbox, i) GetAndCountMessages(s.T, s.store, mailbox, i)
DeliverToStore(t, store, mailbox, subj, time.Now()) DeliverToStore(s.T, s.store, mailbox, subj, time.Now())
} }
// Confirm delivery order. // Confirm delivery order.
msgs := GetAndCountMessages(t, store, mailbox, 5) msgs := GetAndCountMessages(s.T, s.store, mailbox, 5)
for i, want := range subjects { for i, want := range subjects {
got := msgs[i].Subject() got := msgs[i].Subject()
if got != want { if got != want {
t.Errorf("Got subject %q, want %q", got, want) s.Errorf("Got subject %q, want %q", got, want)
} }
} }
} }
// testLatest delivers several messages to the same mailbox, and confirms the id `latest` returns // testLatest delivers several messages to the same mailbox, and confirms the id `latest` returns
// the last message sent. // the last message sent.
func testLatest(t *testing.T, store storage.Store, extHost *extension.Host) { func testLatest(s suite) {
mailbox := "fred" mailbox := "fred"
subjects := []string{"alpha", "bravo", "charlie", "delta", "echo"} subjects := []string{"alpha", "bravo", "charlie", "delta", "echo"}
for _, subj := range subjects { for _, subj := range subjects {
DeliverToStore(t, store, mailbox, subj, time.Now()) DeliverToStore(s.T, s.store, mailbox, subj, time.Now())
} }
// Confirm latest. // Confirm latest.
latest, err := store.GetMessage(mailbox, "latest") latest, err := s.store.GetMessage(mailbox, "latest")
if err != nil { if err != nil {
t.Fatal(err) s.Fatal(err)
} }
if latest == nil { if latest == nil {
t.Fatalf("Got nil message, wanted most recent message for %v.", mailbox) s.Fatalf("Got nil message, wanted most recent message for %v.", mailbox)
} }
got := latest.Subject() got := latest.Subject()
want := "echo" want := "echo"
if got != want { if got != want {
t.Errorf("Got subject %q, want %q", got, want) s.Errorf("Got subject %q, want %q", got, want)
} }
} }
// testNaming ensures the store does not enforce local part mailbox naming. // testNaming ensures the store does not enforce local part mailbox naming.
func testNaming(t *testing.T, store storage.Store, extHost *extension.Host) { func testNaming(s suite) {
DeliverToStore(t, store, "fred@fish.net", "disk #27", time.Now()) DeliverToStore(s.T, s.store, "fred@fish.net", "disk #27", time.Now())
GetAndCountMessages(t, store, "fred", 0) GetAndCountMessages(s.T, s.store, "fred", 0)
GetAndCountMessages(t, store, "fred@fish.net", 1) GetAndCountMessages(s.T, s.store, "fred@fish.net", 1)
} }
// testSize verifies message content size metadata values. // testSize verifies message content size metadata values.
func testSize(t *testing.T, store storage.Store, extHost *extension.Host) { func testSize(s suite) {
mailbox := "fred" mailbox := "fred"
subjects := []string{"a", "br", "much longer than the others"} subjects := []string{"a", "br", "much longer than the others"}
sentIds := make([]string, len(subjects)) sentIds := make([]string, len(subjects))
sentSizes := make([]int64, len(subjects)) sentSizes := make([]int64, len(subjects))
for i, subj := range subjects { for i, subj := range subjects {
id, size := DeliverToStore(t, store, mailbox, subj, time.Now()) id, size := DeliverToStore(s.T, s.store, mailbox, subj, time.Now())
sentIds[i] = id sentIds[i] = id
sentSizes[i] = size sentSizes[i] = size
} }
for i, id := range sentIds { for i, id := range sentIds {
msg, err := store.GetMessage(mailbox, id) msg, err := s.store.GetMessage(mailbox, id)
if err != nil { if err != nil {
t.Fatal(err) s.Fatal(err)
} }
want := sentSizes[i] want := sentSizes[i]
got := msg.Size() got := msg.Size()
if got != want { if got != want {
t.Errorf("Got size %v, want: %v", got, want) s.Errorf("Got size %v, want: %v", got, want)
} }
} }
} }
// testSeen verifies a message can be marked as seen. // testSeen verifies a message can be marked as seen.
func testSeen(t *testing.T, store storage.Store, extHost *extension.Host) { func testSeen(s suite) {
mailbox := "lisa" mailbox := "lisa"
id1, _ := DeliverToStore(t, store, mailbox, "whatever", time.Now()) id1, _ := DeliverToStore(s.T, s.store, mailbox, "whatever", time.Now())
id2, _ := DeliverToStore(t, store, mailbox, "hello?", time.Now()) id2, _ := DeliverToStore(s.T, s.store, mailbox, "hello?", time.Now())
// Confirm unseen. // Confirm unseen.
msg, err := store.GetMessage(mailbox, id1) msg, err := s.store.GetMessage(mailbox, id1)
if err != nil { if err != nil {
t.Fatal(err) s.Fatal(err)
} }
if msg.Seen() { if msg.Seen() {
t.Errorf("got seen %v, want: false", msg.Seen()) s.Errorf("got seen %v, want: false", msg.Seen())
} }
// Mark id1 seen. // Mark id1 seen.
err = store.MarkSeen(mailbox, id1) err = s.store.MarkSeen(mailbox, id1)
if err != nil { if err != nil {
t.Fatal(err) s.Fatal(err)
} }
// Verify id1 seen. // Verify id1 seen.
msg, err = store.GetMessage(mailbox, id1) msg, err = s.store.GetMessage(mailbox, id1)
if err != nil { if err != nil {
t.Fatal(err) s.Fatal(err)
} }
if !msg.Seen() { if !msg.Seen() {
t.Errorf("id1 got seen %v, want: true", msg.Seen()) s.Errorf("id1 got seen %v, want: true", msg.Seen())
} }
// Verify id2 still unseen. // Verify id2 still unseen.
msg, err = store.GetMessage(mailbox, id2) msg, err = s.store.GetMessage(mailbox, id2)
if err != nil { if err != nil {
t.Fatal(err) s.Fatal(err)
} }
if msg.Seen() { if msg.Seen() {
t.Errorf("id2 got seen %v, want: false", msg.Seen()) s.Errorf("id2 got seen %v, want: false", msg.Seen())
} }
} }
// testDelete creates and deletes some messages. // testDelete creates and deletes some messages.
func testDelete(t *testing.T, store storage.Store, extHost *extension.Host) { func testDelete(s suite) {
mailbox := "fred" mailbox := "fred"
subjects := []string{"alpha", "bravo", "charlie", "delta", "echo"} subjects := []string{"alpha", "bravo", "charlie", "delta", "echo"}
for _, subj := range subjects { for _, subj := range subjects {
DeliverToStore(t, store, mailbox, subj, time.Now()) DeliverToStore(s.T, s.store, mailbox, subj, time.Now())
} }
msgs := GetAndCountMessages(t, store, mailbox, len(subjects)) msgs := GetAndCountMessages(s.T, s.store, mailbox, len(subjects))
// Subscribe to events. // Subscribe to events.
eventListener := extHost.Events.AfterMessageDeleted.AsyncTestListener("test", 2) eventListener := s.extHost.Events.AfterMessageDeleted.AsyncTestListener("test", 2)
// Delete a couple messages. // Delete a couple messages.
deleteIDs := []string{msgs[1].ID(), msgs[3].ID()} deleteIDs := []string{msgs[1].ID(), msgs[3].ID()}
for _, id := range deleteIDs { for _, id := range deleteIDs {
err := store.RemoveMessage(mailbox, id) err := s.store.RemoveMessage(mailbox, id)
require.NoError(t, err) require.NoError(s, err)
} }
// Confirm deletion. // Confirm deletion.
subjects = []string{"alpha", "charlie", "echo"} subjects = []string{"alpha", "charlie", "echo"}
msgs = GetAndCountMessages(t, store, mailbox, len(subjects)) msgs = GetAndCountMessages(s.T, s.store, mailbox, len(subjects))
for i, want := range subjects { for i, want := range subjects {
got := msgs[i].Subject() got := msgs[i].Subject()
if got != want { if got != want {
t.Errorf("Got subject %q, want %q", got, want) s.Errorf("Got subject %q, want %q", got, want)
} }
} }
// Capture events and check correct IDs were emitted. // Capture events and check correct IDs were emitted.
ev1, err := eventListener() ev1, err := eventListener()
require.NoError(t, err) require.NoError(s, err)
ev2, err := eventListener() ev2, err := eventListener()
require.NoError(t, err) require.NoError(s, err)
eventIDs := []string{ev1.ID, ev2.ID} eventIDs := []string{ev1.ID, ev2.ID}
for _, id := range deleteIDs { for _, id := range deleteIDs {
assert.Contains(t, eventIDs, id) assert.Contains(s, eventIDs, id)
} }
// Try appending one more. // Try appending one more.
DeliverToStore(t, store, mailbox, "foxtrot", time.Now()) DeliverToStore(s.T, s.store, mailbox, "foxtrot", time.Now())
subjects = []string{"alpha", "charlie", "echo", "foxtrot"} subjects = []string{"alpha", "charlie", "echo", "foxtrot"}
msgs = GetAndCountMessages(t, store, mailbox, len(subjects)) msgs = GetAndCountMessages(s.T, s.store, mailbox, len(subjects))
for i, want := range subjects { for i, want := range subjects {
got := msgs[i].Subject() got := msgs[i].Subject()
if got != want { if got != want {
t.Errorf("Got subject %q, want %q", got, want) s.Errorf("Got subject %q, want %q", got, want)
} }
} }
} }
// testPurge makes sure mailboxes can be purged. // testPurge makes sure mailboxes can be purged.
func testPurge(t *testing.T, store storage.Store, extHost *extension.Host) { func testPurge(s suite) {
mailbox := "fred" mailbox := "fred"
subjects := []string{"alpha", "bravo", "charlie", "delta", "echo"} subjects := []string{"alpha", "bravo", "charlie", "delta", "echo"}
// Subscribe to events. // Subscribe to events.
eventListener := extHost.Events.AfterMessageDeleted.AsyncTestListener("test", len(subjects)) eventListener := s.extHost.Events.AfterMessageDeleted.AsyncTestListener("test", len(subjects))
// Populate mailbox. // Populate mailbox.
for _, subj := range subjects { for _, subj := range subjects {
DeliverToStore(t, store, mailbox, subj, time.Now()) DeliverToStore(s.T, s.store, mailbox, subj, time.Now())
} }
GetAndCountMessages(t, store, mailbox, len(subjects)) GetAndCountMessages(s.T, s.store, mailbox, len(subjects))
// Purge and verify. // Purge and verify.
err := store.PurgeMessages(mailbox) err := s.store.PurgeMessages(mailbox)
require.NoError(t, err) require.NoError(s, err)
GetAndCountMessages(t, store, mailbox, 0) GetAndCountMessages(s.T, s.store, mailbox, 0)
// Confirm events emitted. // Confirm events emitted.
gotEvents := []*event.MessageMetadata{} gotEvents := []*event.MessageMetadata{}
for range subjects { for range subjects {
ev, err := eventListener() ev, err := eventListener()
if err != nil { if err != nil {
t.Error(err) s.Error(err)
break break
} }
gotEvents = append(gotEvents, ev) gotEvents = append(gotEvents, ev)
} }
assert.Equal(t, len(subjects), len(gotEvents), assert.Equal(s, len(subjects), len(gotEvents),
"expected delete event for each message in mailbox") "expected delete event for each message in mailbox")
} }
// testMsgCap verifies the message cap is enforced. // testMsgCap verifies the message cap is enforced.
func testMsgCap(t *testing.T, store storage.Store, extHost *extension.Host) { func testMsgCap(s suite) {
mbCap := 10 mbCap := 10
mailbox := "captain" mailbox := "captain"
for i := 0; i < 20; i++ { for i := 0; i < 20; i++ {
subj := fmt.Sprintf("subject %v", i) subj := fmt.Sprintf("subject %v", i)
DeliverToStore(t, store, mailbox, subj, time.Now()) DeliverToStore(s.T, s.store, mailbox, subj, time.Now())
msgs, err := store.GetMessages(mailbox) msgs, err := s.store.GetMessages(mailbox)
if err != nil { if err != nil {
t.Fatalf("Failed to GetMessages for %q: %v", mailbox, err) s.Fatalf("Failed to GetMessages for %q: %v", mailbox, err)
} }
if len(msgs) > mbCap { if len(msgs) > mbCap {
t.Errorf("Mailbox has %v messages, should be capped at %v", len(msgs), mbCap) s.Errorf("Mailbox has %v messages, should be capped at %v", len(msgs), mbCap)
break break
} }
// Check that the first message is correct.
// Check that the first (oldest) message is correct.
first := i - mbCap + 1 first := i - mbCap + 1
if first < 0 { if first < 0 {
first = 0 first = 0
} }
firstSubj := fmt.Sprintf("subject %v", first) firstSubj := fmt.Sprintf("subject %v", first)
if firstSubj != msgs[0].Subject() { if firstSubj != msgs[0].Subject() {
t.Errorf("Got subject %q, wanted first subject: %q", msgs[0].Subject(), firstSubj) s.Errorf("Got subject %q, wanted first subject: %q", msgs[0].Subject(), firstSubj)
} }
} }
} }
// testNoMsgCap verfies a cap of 0 is not enforced. // testNoMsgCap verfies a cap of 0 is not enforced.
func testNoMsgCap(t *testing.T, store storage.Store, extHost *extension.Host) { func testNoMsgCap(s suite) {
mailbox := "captain" mailbox := "captain"
for i := 0; i < 20; i++ { for i := 0; i < 20; i++ {
subj := fmt.Sprintf("subject %v", i) subj := fmt.Sprintf("subject %v", i)
DeliverToStore(t, store, mailbox, subj, time.Now()) DeliverToStore(s.T, s.store, mailbox, subj, time.Now())
GetAndCountMessages(t, store, mailbox, i+1) GetAndCountMessages(s.T, s.store, mailbox, i+1)
} }
} }
// testVisitMailboxes creates some mailboxes and confirms the VisitMailboxes method visits all of // testVisitMailboxes creates some mailboxes and confirms the VisitMailboxes method visits all of
// them. // them.
func testVisitMailboxes(t *testing.T, ds storage.Store, extHost *extension.Host) { func testVisitMailboxes(s suite) {
// Deliver 2 test messages to each of 5 mailboxes. // Deliver 2 test messages to each of 5 mailboxes.
boxes := []string{"abby", "bill", "christa", "donald", "evelyn"} boxes := []string{"abby", "bill", "christa", "donald", "evelyn"}
for _, name := range boxes { for _, name := range boxes {
DeliverToStore(t, ds, name, "Old Message", time.Now().Add(-24*time.Hour)) DeliverToStore(s.T, s.store, name, "Old Message", time.Now().Add(-24*time.Hour))
DeliverToStore(t, ds, name, "New Message", time.Now()) DeliverToStore(s.T, s.store, name, "New Message", time.Now())
} }
// Verify message and mailbox counts. // Verify message and mailbox counts.
nboxes := 0 nboxes := 0
err := ds.VisitMailboxes(func(messages []storage.Message) bool { err := s.store.VisitMailboxes(func(messages []storage.Message) bool {
nboxes++ nboxes++
name := "unknown" name := "unknown"
if len(messages) > 0 { if len(messages) > 0 {
name = messages[0].Mailbox() name = messages[0].Mailbox()
} }
assert.Len(t, messages, 2, "incorrect message count in mailbox %s", name) assert.Len(s, messages, 2, "incorrect message count in mailbox %s", name)
return true return true
}) })
require.NoError(t, err, "VisitMailboxes() failed") require.NoError(s, err, "VisitMailboxes() failed")
assert.Equal(t, 5, nboxes, "visited %v mailboxes, want: 5", nboxes) assert.Equal(s, 5, nboxes, "visited %v mailboxes, want: 5", nboxes)
}
// DeliverToStore creates and delivers a message to the specific mailbox, returning the size of the
// generated message.
func DeliverToStore(
t *testing.T,
store storage.Store,
mailbox string,
subject string,
date time.Time,
) (string, int64) {
t.Helper()
meta := event.MessageMetadata{
Mailbox: mailbox,
To: []*mail.Address{{Name: "Some Body", Address: "somebody@host"}},
From: &mail.Address{Name: "Some B. Else", Address: "somebodyelse@host"},
Subject: subject,
Date: date,
}
testMsg := fmt.Sprintf("To: %s\r\nFrom: %s\r\nSubject: %s\r\n\r\nTest Body\r\n",
meta.To[0].Address, meta.From.Address, subject)
delivery := &message.Delivery{
Meta: meta,
Reader: io.NopCloser(strings.NewReader(testMsg)),
}
id, err := store.AddMessage(delivery)
if err != nil {
t.Fatal(err)
}
return id, int64(len(testMsg))
}
// GetAndCountMessages is a test helper that expects to receive count messages or fails the test, it
// also checks return error.
func GetAndCountMessages(t *testing.T, s storage.Store, mailbox string, count int) []storage.Message {
t.Helper()
msgs, err := s.GetMessages(mailbox)
if err != nil {
t.Fatalf("Failed to GetMessages for %q: %v", mailbox, err)
}
if len(msgs) != count {
t.Errorf("Got %v messages for %q, want: %v", len(msgs), mailbox, count)
}
return msgs
} }