188 lines
4.3 KiB
Go
188 lines
4.3 KiB
Go
package client
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"io"
|
|
"net"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/emersion/go-imap"
|
|
)
|
|
|
|
type cmdScanner struct {
|
|
scanner *bufio.Scanner
|
|
}
|
|
|
|
func (s *cmdScanner) ScanLine() string {
|
|
s.scanner.Scan()
|
|
return s.scanner.Text()
|
|
}
|
|
|
|
func (s *cmdScanner) ScanCmd() (tag string, cmd string) {
|
|
parts := strings.SplitN(s.ScanLine(), " ", 2)
|
|
return parts[0], parts[1]
|
|
}
|
|
|
|
func newCmdScanner(r io.Reader) *cmdScanner {
|
|
return &cmdScanner{
|
|
scanner: bufio.NewScanner(r),
|
|
}
|
|
}
|
|
|
|
type serverConn struct {
|
|
*cmdScanner
|
|
net.Conn
|
|
net.Listener
|
|
}
|
|
|
|
func (c *serverConn) Close() error {
|
|
if err := c.Conn.Close(); err != nil {
|
|
return err
|
|
}
|
|
return c.Listener.Close()
|
|
}
|
|
|
|
func (c *serverConn) WriteString(s string) (n int, err error) {
|
|
return io.WriteString(c.Conn, s)
|
|
}
|
|
|
|
func newTestClient(t *testing.T) (c *Client, s *serverConn) {
|
|
return newTestClientWithGreeting(t, "* OK [CAPABILITY IMAP4rev1 STARTTLS AUTH=PLAIN UNSELECT] Server ready.\r\n")
|
|
}
|
|
|
|
func newTestClientWithGreeting(t *testing.T, greeting string) (c *Client, s *serverConn) {
|
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
conn, err := l.Accept()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if _, err := io.WriteString(conn, greeting); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
s = &serverConn{newCmdScanner(conn), conn, l}
|
|
close(done)
|
|
}()
|
|
|
|
c, err = Dial(l.Addr().String())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
<-done
|
|
return
|
|
}
|
|
|
|
func setClientState(c *Client, state imap.ConnState, mailbox *imap.MailboxStatus) {
|
|
c.locker.Lock()
|
|
c.state = state
|
|
c.mailbox = mailbox
|
|
c.locker.Unlock()
|
|
}
|
|
|
|
func TestClient(t *testing.T) {
|
|
c, s := newTestClient(t)
|
|
defer s.Close()
|
|
|
|
if ok, err := c.Support("IMAP4rev1"); err != nil {
|
|
t.Fatal("c.Support(IMAP4rev1) =", err)
|
|
} else if !ok {
|
|
t.Fatal("c.Support(IMAP4rev1) = false, want true")
|
|
}
|
|
}
|
|
|
|
func TestClient_SetDebug(t *testing.T) {
|
|
c, s := newTestClient(t)
|
|
defer s.Close()
|
|
|
|
var b bytes.Buffer
|
|
done := make(chan error)
|
|
|
|
go func() {
|
|
c.SetDebug(&b)
|
|
done <- nil
|
|
}()
|
|
if tag, cmd := s.ScanCmd(); cmd != "NOOP" {
|
|
t.Fatal("Bad command:", cmd)
|
|
} else {
|
|
s.WriteString(tag + " OK NOOP completed.\r\n")
|
|
}
|
|
// wait for SetDebug to finish.
|
|
<-done
|
|
|
|
go func() {
|
|
_, err := c.Capability()
|
|
done <- err
|
|
}()
|
|
|
|
tag, cmd := s.ScanCmd()
|
|
if cmd != "CAPABILITY" {
|
|
t.Fatal("Bad command:", cmd)
|
|
}
|
|
|
|
s.WriteString("* CAPABILITY IMAP4rev1\r\n")
|
|
s.WriteString(tag + " OK CAPABILITY completed.\r\n")
|
|
|
|
if err := <-done; err != nil {
|
|
t.Fatal("c.Capability() =", err)
|
|
}
|
|
|
|
if b.Len() == 0 {
|
|
t.Error("empty debug buffer")
|
|
}
|
|
}
|
|
|
|
func TestClient_unilateral(t *testing.T) {
|
|
c, s := newTestClient(t)
|
|
defer s.Close()
|
|
|
|
setClientState(c, imap.SelectedState, imap.NewMailboxStatus("INBOX", nil))
|
|
|
|
updates := make(chan Update, 1)
|
|
c.Updates = updates
|
|
|
|
s.WriteString("* 42 EXISTS\r\n")
|
|
if update, ok := (<-updates).(*MailboxUpdate); !ok || update.Mailbox.Messages != 42 {
|
|
t.Errorf("Invalid messages count: expected %v but got %v", 42, update.Mailbox.Messages)
|
|
}
|
|
|
|
s.WriteString("* 587 RECENT\r\n")
|
|
if update, ok := (<-updates).(*MailboxUpdate); !ok || update.Mailbox.Recent != 587 {
|
|
t.Errorf("Invalid recent count: expected %v but got %v", 587, update.Mailbox.Recent)
|
|
}
|
|
|
|
s.WriteString("* 65535 EXPUNGE\r\n")
|
|
if update, ok := (<-updates).(*ExpungeUpdate); !ok || update.SeqNum != 65535 {
|
|
t.Errorf("Invalid expunged sequence number: expected %v but got %v", 65535, update.SeqNum)
|
|
}
|
|
|
|
s.WriteString("* 431 FETCH (FLAGS (\\Seen))\r\n")
|
|
if update, ok := (<-updates).(*MessageUpdate); !ok || update.Message.SeqNum != 431 {
|
|
t.Errorf("Invalid expunged sequence number: expected %v but got %v", 431, update.Message.SeqNum)
|
|
}
|
|
|
|
s.WriteString("* OK Reticulating splines...\r\n")
|
|
if update, ok := (<-updates).(*StatusUpdate); !ok || update.Status.Info != "Reticulating splines..." {
|
|
t.Errorf("Invalid info: got %v", update.Status.Info)
|
|
}
|
|
|
|
s.WriteString("* NO Kansai band competition is in 30 seconds !\r\n")
|
|
if update, ok := (<-updates).(*StatusUpdate); !ok || update.Status.Info != "Kansai band competition is in 30 seconds !" {
|
|
t.Errorf("Invalid warning: got %v", update.Status.Info)
|
|
}
|
|
|
|
s.WriteString("* BAD Battery level too low, shutting down.\r\n")
|
|
if update, ok := (<-updates).(*StatusUpdate); !ok || update.Status.Info != "Battery level too low, shutting down." {
|
|
t.Errorf("Invalid error: got %v", update.Status.Info)
|
|
}
|
|
}
|