14 func _fl(depth int) string {
15 _, file, line, _ := runtime.Caller(depth + 1)
16 return fmt.Sprintf("[%s:%d]", filepath.Base(file), line)
19 func ok(t testing.TB, err error) {
21 t.Errorf("%s unexpected error: %v", _fl(1), err)
25 func responseOK(t testing.TB, conn *textproto.Conn) string {
26 line, err := conn.ReadLine()
28 t.Errorf("%s responseOK: %v", _fl(1), err)
30 if !strings.HasPrefix(line, "+OK") {
31 t.Errorf("%s expected +OK, got %q", _fl(1), line)
36 func responseERR(t testing.TB, conn *textproto.Conn) string {
37 line, err := conn.ReadLine()
39 t.Errorf("%s responseERR: %v", _fl(1), err)
41 if !strings.HasPrefix(line, "-ERR") {
42 t.Errorf("%s expected -ERR, got %q", _fl(1), line)
47 func runServer(t *testing.T, po PostOffice) net.Listener {
48 l, err := net.Listen("tcp", "localhost:0")
56 conn, err := l.Accept()
60 go AcceptConnection(conn, po)
66 type testServer struct {
71 func (s *testServer) Name() string {
75 func (s *testServer) OpenMailbox(user, pass string) (Mailbox, error) {
76 if s.user == user && s.pass == pass {
79 return nil, fmt.Errorf("bad username/pass")
82 type testMailbox struct {
83 msgs map[int]*testMessage
86 func (mb *testMailbox) ListMessages() ([]Message, error) {
87 msgs := make([]Message, 0, len(mb.msgs))
88 for i, _ := range mb.msgs {
89 msgs = append(msgs, mb.msgs[i])
94 func (mb *testMailbox) GetMessage(id int) Message {
98 func (mb *testMailbox) Retrieve(msg Message) (io.ReadCloser, error) {
102 func (mb *testMailbox) Delete(msg Message) error {
103 msg.(*testMessage).deleted = true
107 func (mb *testMailbox) Close() error {
111 func (mb *testMailbox) Reset() {
112 for _, msg := range mb.msgs {
117 type testMessage struct {
123 func (m *testMessage) ID() int {
126 func (m *testMessage) Size() int {
129 func (m *testMessage) Deleted() bool {
133 func newTestServer() *testServer {
138 msgs: make(map[int]*testMessage),
144 func TestExampleSession(t *testing.T) {
149 s.mb.msgs[1] = &testMessage{1, 120, false}
150 s.mb.msgs[2] = &testMessage{2, 200, false}
152 conn, err := textproto.Dial(l.Addr().Network(), l.Addr().String())
155 line := responseOK(t, conn)
156 if !strings.Contains(line, s.Name()) {
157 t.Errorf("POP greeting did not include server name, got %q", line)
160 ok(t, conn.PrintfLine("USER u"))
163 ok(t, conn.PrintfLine("PASS p"))
166 ok(t, conn.PrintfLine("STAT"))
167 line = responseOK(t, conn)
168 expected := "+OK 2 320"
169 if line != expected {
170 t.Errorf("STAT expected %q, got %q", expected, line)
173 ok(t, conn.PrintfLine("LIST"))
175 lines, err := conn.ReadDotLines()
178 t.Errorf("LIST expected 2 lines, got %d", len(lines))
181 if lines[0] != expected {
182 t.Errorf("LIST line 0 expected %q, got %q", expected, lines[0])
185 if lines[1] != expected {
186 t.Errorf("LIST line 1 expected %q, got %q", expected, lines[1])
189 ok(t, conn.PrintfLine("QUIT"))
193 type requestResponse struct {
195 expecter func(testing.TB, *textproto.Conn) string
198 func expectOKResponse(predicate func(string) bool) func(testing.TB, *textproto.Conn) string {
199 return func(t testing.TB, conn *textproto.Conn) string {
200 line := responseOK(t, conn)
201 if !predicate(line) {
202 t.Errorf("%s Predicate failed, got %q", _fl(1), line)
208 func clientServerTest(t *testing.T, s *testServer, sequence []requestResponse) {
212 conn, err := textproto.Dial(l.Addr().Network(), l.Addr().String())
217 for _, pair := range sequence {
218 ok(t, conn.PrintfLine(pair.command))
219 pair.expecter(t, conn)
221 t.Logf("command %q", pair.command)
226 func TestAuthStates(t *testing.T) {
227 clientServerTest(t, newTestServer(), []requestResponse{
228 {"STAT", responseERR},
229 {"NOOP", responseOK},
230 {"USER bad", responseOK},
231 {"PASS bad", responseERR},
232 {"LIST", responseERR},
233 {"USER u", responseOK},
234 {"PASS bad", responseERR},
235 {"STAT", responseERR},
236 {"PASS p", responseOK},
237 {"QUIT", responseOK},
241 func TestDeleted(t *testing.T) {
243 s.mb.msgs[1] = &testMessage{1, 999, false}
244 s.mb.msgs[2] = &testMessage{2, 10, false}
246 clientServerTest(t, s, []requestResponse{
247 {"USER u", responseOK},
248 {"PASS p", responseOK},
249 {"STAT", expectOKResponse(func(s string) bool {
250 return s == "+OK 2 1009"
252 {"DELE 1", responseOK},
253 {"RETR 1", responseERR},
254 {"DELE 1", responseERR},
255 {"STAT", expectOKResponse(func(s string) bool {
256 return s == "+OK 1 10"
258 {"RSET", responseOK},
259 {"STAT", expectOKResponse(func(s string) bool {
260 return s == "+OK 2 1009"
262 {"QUIT", responseOK},
266 func TestCaseSensitivty(t *testing.T) {
268 s.mb.msgs[999] = &testMessage{999, 1, false}
270 clientServerTest(t, s, []requestResponse{
271 {"user u", responseOK},
272 {"PasS p", responseOK},
273 {"sTaT", responseOK},
274 {"retr 1", responseERR},
275 {"dele 999", responseOK},
276 {"QUIT", responseOK},