Add simple Makefile for common tasks.
[mailpopbox.git] / pop3_test.go
1 // mailpopbox
2 // Copyright 2020 Blue Static <https://www.bluestatic.org>
3 // This program is free software licensed under the GNU General Public License,
4 // version 3.0. The full text of the license can be found in LICENSE.txt.
5 // SPDX-License-Identifier: GPL-3.0-only
6
7 package main
8
9 import (
10 "io/ioutil"
11 "net/textproto"
12 "os"
13 "path/filepath"
14 "testing"
15
16 "go.uber.org/zap"
17 )
18
19 func TestReset(t *testing.T) {
20 mbox := mailbox{
21 messages: []message{
22 {"msg1", 1, 4, false},
23 {"msg2", 2, 4, false},
24 },
25 }
26
27 msg := mbox.GetMessage(1)
28 if msg == nil {
29 t.Errorf("failed to GetMessage #1")
30 }
31
32 if msg.Deleted() {
33 t.Errorf("message %v should not be deleted", msg)
34 }
35
36 if err := mbox.Delete(msg); err != nil {
37 t.Error(err)
38 }
39
40 if !msg.Deleted() {
41 t.Errorf("message %v should be deleted", msg)
42 }
43
44 mbox.Reset()
45
46 if msg.Deleted() {
47 t.Errorf("reset did not un-delete message %v", msg)
48 }
49 }
50
51 func TestOpenMailboxAuth(t *testing.T) {
52 dir, err := ioutil.TempDir("", "maildrop")
53 if err != nil {
54 t.Errorf("Failed to create temp dir: %v", err)
55 return
56 }
57 defer os.RemoveAll(dir)
58
59 s := &pop3Server{
60 config: Config{
61 Servers: []Server{
62 {
63 Domain: "example.com",
64 MailboxPassword: "letmein",
65 MaildropPath: dir,
66 },
67 {
68 Domain: "test.net",
69 MailboxPassword: "open-sesame",
70 MaildropPath: dir,
71 },
72 },
73 },
74 log: zap.NewNop(),
75 }
76
77 cases := []struct {
78 user, pass string
79 ok bool
80 }{
81 {"mailbox@example.com", "letmein", true},
82 {"mailbox@test.net", "open-sesame", true},
83 {"mailbox@example.com", "open-sesame", false},
84 {"test@test.net", "open-sesame", false},
85 {"mailbox@an-example.net", "letmein", false},
86 }
87 for i, c := range cases {
88 mb, err := s.OpenMailbox(c.user, c.pass)
89 actual := (mb != nil && err == nil)
90 if actual != c.ok {
91 t.Errorf("Expected error=%v for case %d (%#v), got %v (error=%v, mb=%v)", c.ok, i, c, actual, err, mb)
92 }
93 }
94 }
95
96 func TestBasicListener(t *testing.T) {
97 dir, err := ioutil.TempDir("", "maildrop")
98 if err != nil {
99 t.Errorf("Failed to create temp dir: %v", err)
100 return
101 }
102 defer os.RemoveAll(dir)
103
104 s := &pop3Server{
105 config: Config{
106 POP3Port: 9648,
107 Hostname: "example.com",
108 Servers: []Server{
109 {
110 Domain: "example.com",
111 MaildropPath: dir,
112 },
113 },
114 },
115 log: zap.NewNop(),
116 }
117
118 go s.run()
119
120 conn, err := textproto.Dial("tcp", "localhost:9648")
121 if err != nil {
122 t.Errorf("Failed to dial test server: %v", err)
123 return
124 }
125
126 _, err = conn.ReadLine()
127 if err != nil {
128 t.Errorf("Failed to read line: %v\n", err)
129 return
130 }
131 }
132
133 func TestMailbox(t *testing.T) {
134 dir, err := ioutil.TempDir("", "maildrop")
135 if err != nil {
136 t.Errorf("Failed to create temp dir: %v", err)
137 return
138 }
139 defer os.RemoveAll(dir)
140
141 // Create the first message.
142 f, err := os.Create(filepath.Join(dir, "a.msg"))
143 if err != nil {
144 t.Errorf("Failed to create a.msg: %v", err)
145 return
146 }
147 for i := 0; i < 1024*10; i++ {
148 buf := []byte{'a'}
149 _, err = f.Write(buf)
150 if err != nil {
151 t.Errorf("Failed to write a.msg: %v", err)
152 }
153 }
154 f.Close()
155
156 // Create the second message.
157 f, err = os.Create(filepath.Join(dir, "b.msg"))
158 if err != nil {
159 t.Errorf("Failed to create b.msg: %v", err)
160 return
161 }
162 for i := 0; i < 1024*3; i++ {
163 buf := []byte{'z'}
164 _, err = f.Write(buf)
165 if err != nil {
166 t.Errorf("Failed to write z.msg: %v", err)
167 }
168 }
169 f.Close()
170
171 s := &pop3Server{
172 config: Config{
173 Servers: []Server{
174 {
175 Domain: "example.com",
176 MailboxPassword: "letmein",
177 MaildropPath: dir,
178 },
179 },
180 },
181 log: zap.NewNop(),
182 }
183
184 // Test message metadata.
185 mb, err := s.OpenMailbox("mailbox@example.com", "letmein")
186 if err != nil {
187 t.Errorf("Failed to open mailbox: %v", err)
188 }
189
190 msgs, err := mb.ListMessages()
191 if err != nil {
192 t.Errorf("Failed to list messages: %v", err)
193 }
194
195 if len(msgs) != 2 {
196 t.Errorf("Expected 2 messages, got %d", len(msgs))
197 }
198
199 if mb.GetMessage(0) != nil {
200 t.Errorf("Messages should be 1-indexed")
201 }
202 if mb.GetMessage(3) != nil {
203 t.Errorf("Retreived unexpected message")
204 }
205
206 if msgs[0] != mb.GetMessage(msgs[0].ID()) {
207 t.Errorf("Failed to look up message by ID")
208 }
209
210 if msgs[0].UniqueID() != "a" {
211 t.Errorf("Expected message #1 unique ID to be a, got %s", msgs[0].UniqueID())
212 }
213 expectedSize := 1024 * 10
214 if msgs[0].Size() != expectedSize {
215 t.Errorf("Expected message #1 size to be %v, got %v", expectedSize, msgs[0].Size())
216 }
217
218 if msgs[1].UniqueID() != "b" {
219 t.Errorf("Expected message #2 unique ID to be b, got %s", msgs[0].UniqueID())
220 }
221 expectedSize = 1024 * 3
222 if msgs[1].Size() != expectedSize {
223 t.Errorf("Expected message #2 size to be %v, got %v", expectedSize, msgs[0].Size())
224 }
225
226 // Test message contents.
227 rc, err := mb.Retrieve(msgs[0])
228 if err != nil {
229 t.Errorf("Failed to retrieve message: %v", err)
230 }
231 rc.Close()
232
233 // Test deletion marking and reset.
234 err = mb.Delete(msgs[1])
235 if err != nil {
236 t.Errorf("Failed to mark message #2 for deletion: %v", err)
237 }
238
239 if !msgs[1].Deleted() {
240 t.Errorf("Message should be marked for deletion and isn't")
241 }
242
243 mb.Reset()
244
245 if msgs[1].Deleted() {
246 t.Errorf("Message is marked for deletion and shouldn't be")
247 }
248
249 // Test deletion for real.
250 err = mb.Delete(msgs[0])
251 if err != nil {
252 t.Errorf("Failed to mark message for deletion: %v", err)
253 }
254
255 err = mb.Close()
256 if err != nil {
257 t.Errorf("Failed to close mailbox: %v", err)
258 }
259
260 mb, err = s.OpenMailbox("mailbox@example.com", "letmein")
261 if err != nil {
262 t.Errorf("Failed to re-open mailbox: %v", err)
263 }
264
265 msgs, err = mb.ListMessages()
266 if err != nil {
267 t.Errorf("Failed to list messages: %v", err)
268 }
269
270 if len(msgs) != 1 {
271 t.Errorf("Number of messages should be 1, got %d", len(msgs))
272 }
273
274 if msgs[0].UniqueID() != "b" {
275 t.Errorf("Message Unique ID should be b, got %s", msgs[0].UniqueID())
276 }
277 }