mirror of
https://github.com/mjl-/mox.git
synced 2024-12-26 00:13:47 +03:00
03c3f56a59
this doesn't really test the output of the ctl commands, just that they succeed without error. better than nothing... testing found two small bugs, that are not an issue in practice: 1. we were ack'ing streamed data from the other side of the ctl connection before having read it. when there is no buffer space on the connection (always the case for net.Pipe) that would cause a deadlock. only actually happened during the new tests. 2. the generated dkim keys are relatively to the directory of the dynamic config file. mox looked it up relative to the directory of the _static_ config file at startup. this directory is typicaly the same. users would have noticed if they had triggered this.
180 lines
5.2 KiB
Go
180 lines
5.2 KiB
Go
//go:build integration
|
|
|
|
// Run this using docker-compose.yml, see Makefile.
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/mjl-/mox/dns"
|
|
"github.com/mjl-/mox/imapclient"
|
|
"github.com/mjl-/mox/mlog"
|
|
"github.com/mjl-/mox/mox-"
|
|
"github.com/mjl-/mox/sasl"
|
|
"github.com/mjl-/mox/smtpclient"
|
|
"github.com/mjl-/mox/store"
|
|
)
|
|
|
|
var ctxbg = context.Background()
|
|
|
|
func tcheck(t *testing.T, err error, errmsg string) {
|
|
if err != nil {
|
|
t.Fatalf("%s: %s", errmsg, err)
|
|
}
|
|
}
|
|
|
|
// Submit a message to mox, which sends it to postfix, which forwards back to mox.
|
|
// We check if we receive the message.
|
|
func TestDeliver(t *testing.T) {
|
|
mlog.Logfmt = true
|
|
log := mlog.New("test")
|
|
|
|
// Remove state.
|
|
os.RemoveAll("testdata/integration/data")
|
|
os.MkdirAll("testdata/integration/data", 0750)
|
|
|
|
// Cleanup afterwards, these are owned by root, annoying to have around due to
|
|
// permission errors.
|
|
defer os.RemoveAll("testdata/integration/data")
|
|
|
|
// Load mox config.
|
|
mox.ConfigStaticPath = "testdata/integration/config/mox.conf"
|
|
if errs := mox.LoadConfig(ctxbg, true, false); len(errs) > 0 {
|
|
t.Fatalf("loading mox config: %v", errs)
|
|
}
|
|
|
|
// Create new accounts
|
|
createAccount := func(email, password string) {
|
|
t.Helper()
|
|
acc, _, err := store.OpenEmail(email)
|
|
tcheck(t, err, "open account")
|
|
err = acc.SetPassword(password)
|
|
tcheck(t, err, "setting password")
|
|
err = acc.Close()
|
|
tcheck(t, err, "closing account")
|
|
}
|
|
|
|
createAccount("moxtest1@mox1.example", "pass1234")
|
|
createAccount("moxtest2@mox2.example", "pass1234")
|
|
createAccount("moxtest3@mox3.example", "pass1234")
|
|
|
|
// Start mox.
|
|
const mtastsdbRefresher = false
|
|
const skipForkExec = true
|
|
err := start(mtastsdbRefresher, skipForkExec)
|
|
tcheck(t, err, "starting mox")
|
|
|
|
// Single update from IMAP IDLE.
|
|
type idleResponse struct {
|
|
untagged imapclient.Untagged
|
|
err error
|
|
}
|
|
|
|
testDeliver := func(checkTime bool, imapaddr, imapuser, imappass string, fn func()) {
|
|
t.Helper()
|
|
|
|
// Make IMAP connection, we'll wait for a delivery notification with IDLE.
|
|
imapconn, err := net.Dial("tcp", imapaddr)
|
|
tcheck(t, err, "dial imap server")
|
|
defer imapconn.Close()
|
|
client, err := imapclient.New(imapconn, false)
|
|
tcheck(t, err, "new imapclient")
|
|
_, _, err = client.Login(imapuser, imappass)
|
|
tcheck(t, err, "imap client login")
|
|
_, _, err = client.Select("inbox")
|
|
tcheck(t, err, "imap select inbox")
|
|
|
|
err = client.Commandf("", "idle")
|
|
tcheck(t, err, "imap idle command")
|
|
|
|
_, _, _, err = client.ReadContinuation()
|
|
tcheck(t, err, "read imap continuation")
|
|
|
|
idle := make(chan idleResponse)
|
|
go func() {
|
|
for {
|
|
untagged, err := client.ReadUntagged()
|
|
idle <- idleResponse{untagged, err}
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
defer func() {
|
|
err := client.Writelinef("done")
|
|
tcheck(t, err, "aborting idle")
|
|
}()
|
|
|
|
t0 := time.Now()
|
|
fn()
|
|
|
|
// Wait for notification of delivery.
|
|
select {
|
|
case resp := <-idle:
|
|
tcheck(t, resp.err, "idle notification")
|
|
_, ok := resp.untagged.(imapclient.UntaggedExists)
|
|
if !ok {
|
|
t.Fatalf("got idle %#v, expected untagged exists", resp.untagged)
|
|
}
|
|
if d := time.Since(t0); checkTime && d < 1*time.Second {
|
|
t.Fatalf("delivery took %v, but should have taken at least 1 second, the first-time sender delay", d)
|
|
}
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatalf("timeout after 5s waiting for IMAP IDLE notification of new message, should take about 1 second")
|
|
}
|
|
}
|
|
|
|
submit := func(smtphost, smtpport, mailfrom, password, rcptto string) {
|
|
conn, err := net.Dial("tcp", net.JoinHostPort(smtphost, smtpport))
|
|
tcheck(t, err, "dial submission")
|
|
defer conn.Close()
|
|
|
|
msg := fmt.Sprintf(`From: <%s>
|
|
To: <%s>
|
|
Subject: test message
|
|
|
|
This is the message.
|
|
`, mailfrom, rcptto)
|
|
msg = strings.ReplaceAll(msg, "\n", "\r\n")
|
|
auth := []sasl.Client{sasl.NewClientPlain(mailfrom, password)}
|
|
c, err := smtpclient.New(mox.Context, log, conn, smtpclient.TLSOpportunistic, mox.Conf.Static.HostnameDomain, dns.Domain{ASCII: smtphost}, auth)
|
|
tcheck(t, err, "smtp hello")
|
|
err = c.Deliver(mox.Context, mailfrom, rcptto, int64(len(msg)), strings.NewReader(msg), false, false)
|
|
tcheck(t, err, "deliver with smtp")
|
|
err = c.Close()
|
|
tcheck(t, err, "close smtpclient")
|
|
}
|
|
|
|
testDeliver(true, "moxmail1.mox1.example:143", "moxtest1@mox1.example", "pass1234", func() {
|
|
submit("moxmail1.mox1.example", "587", "moxtest1@mox1.example", "pass1234", "root@postfix.example")
|
|
})
|
|
testDeliver(true, "moxmail1.mox1.example:143", "moxtest3@mox3.example", "pass1234", func() {
|
|
submit("moxmail2.mox2.example", "587", "moxtest2@mox2.example", "pass1234", "moxtest3@mox3.example")
|
|
})
|
|
|
|
testDeliver(false, "localserve.mox1.example:1143", "mox@localhost", "moxmoxmox", func() {
|
|
submit("localserve.mox1.example", "1587", "mox@localhost", "moxmoxmox", "any@any.example")
|
|
})
|
|
|
|
testDeliver(false, "localserve.mox1.example:1143", "mox@localhost", "moxmoxmox", func() {
|
|
cmd := exec.Command("go", "run", ".", "sendmail", "mox@localhost")
|
|
const msg = `Subject: test
|
|
|
|
a message.
|
|
`
|
|
cmd.Stdin = strings.NewReader(msg)
|
|
var out strings.Builder
|
|
cmd.Stdout = &out
|
|
err := cmd.Run()
|
|
log.Print("sendmail", mlog.Field("output", out.String()))
|
|
tcheck(t, err, "sendmail")
|
|
})
|
|
}
|