services: databases: Don't specify a default postgresql version.
[jackhill/guix/guix.git] / gnu / tests / mail.scm
CommitLineData
a82f0b36
SB
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2016 Sou Bunnbu <iyzsong@member.fsf.org>
950026ac 3;;; Copyright © 2017 Carlo Zancanaro <carlo@zancanaro.id.au>
827abd5d 4;;; Copyright © 2017, 2020 Ludovic Courtès <ludo@gnu.org>
a9079b48 5;;; Copyright © 2018 Oleg Pykhalov <go.wigust@gmail.com>
c24b1547 6;;; Copyright © 2018 Clément Lassieur <clement@lassieur.org>
f6b0e1f8 7;;; Copyright © 2019 Christopher Baines <mail@cbaines.net>
c3f146e7 8;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
a82f0b36
SB
9;;;
10;;; This file is part of GNU Guix.
11;;;
12;;; GNU Guix is free software; you can redistribute it and/or modify it
13;;; under the terms of the GNU General Public License as published by
14;;; the Free Software Foundation; either version 3 of the License, or (at
15;;; your option) any later version.
16;;;
17;;; GNU Guix is distributed in the hope that it will be useful, but
18;;; WITHOUT ANY WARRANTY; without even the implied warranty of
19;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;;; GNU General Public License for more details.
21;;;
22;;; You should have received a copy of the GNU General Public License
23;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
24
25(define-module (gnu tests mail)
26 #:use-module (gnu tests)
bf5929c5 27 #:use-module (gnu packages mail)
a82f0b36 28 #:use-module (gnu system)
827abd5d
LC
29 #:use-module (gnu system accounts)
30 #:use-module (gnu system shadow)
a82f0b36
SB
31 #:use-module (gnu system vm)
32 #:use-module (gnu services)
827abd5d 33 #:use-module (gnu services base)
f6b0e1f8 34 #:use-module (gnu services getmail)
a82f0b36
SB
35 #:use-module (gnu services mail)
36 #:use-module (gnu services networking)
37 #:use-module (guix gexp)
a82f0b36 38 #:use-module (guix store)
950026ac
CZ
39 #:use-module (ice-9 ftw)
40 #:export (%test-opensmtpd
a9079b48 41 %test-exim
f6b0e1f8
CB
42 %test-dovecot
43 %test-getmail))
a82f0b36
SB
44
45(define %opensmtpd-os
892d9089 46 (simple-operating-system
39d7fdce 47 (service dhcp-client-service-type)
892d9089
LC
48 (service opensmtpd-service-type
49 (opensmtpd-configuration
50 (config-file
51 (plain-file "smtpd.conf" "
a82f0b36 52listen on 0.0.0.0
c3f146e7
TGR
53action inbound mbox
54match from any for local action inbound
892d9089 55"))))))
a82f0b36
SB
56
57(define (run-opensmtpd-test)
58 "Return a test of an OS running OpenSMTPD service."
8b113790
LC
59 (define vm
60 (virtual-machine
61 (operating-system (marionette-operating-system
62 %opensmtpd-os
63 #:imported-modules '((gnu services herd))))
64 (port-forwardings '((1025 . 25)))))
65
66 (define test
67 (with-imported-modules '((gnu build marionette))
68 #~(begin
69 (use-modules (rnrs base)
70 (srfi srfi-64)
71 (ice-9 rdelim)
72 (ice-9 regex)
73 (gnu build marionette))
74
75 (define marionette
76 (make-marionette '(#$vm)))
77
78 (define (read-reply-code port)
79 "Read a SMTP reply from PORT and return its reply code."
80 (let* ((line (read-line port))
81 (mo (string-match "([0-9]+)([ -]).*" line))
82 (code (string->number (match:substring mo 1)))
83 (finished? (string= " " (match:substring mo 2))))
84 (if finished?
85 code
86 (read-reply-code port))))
87
88 (mkdir #$output)
89 (chdir #$output)
90
91 (test-begin "opensmptd")
92
93 (test-assert "service is running"
94 (marionette-eval
95 '(begin
96 (use-modules (gnu services herd))
c24b1547 97 (start-service 'smtpd))
8b113790
LC
98 marionette))
99
100 (test-assert "mbox is empty"
101 (marionette-eval
a37e03d6
LC
102 '(and (file-exists? "/var/spool/mail")
103 (not (file-exists? "/var/spool/mail/root")))
8b113790
LC
104 marionette))
105
106 (test-eq "accept an email"
107 #t
108 (let* ((smtp (socket AF_INET SOCK_STREAM 0))
109 (addr (make-socket-address AF_INET INADDR_LOOPBACK 1025)))
110 (connect smtp addr)
111 ;; Be greeted.
112 (read-reply-code smtp) ;220
113 ;; Greet the server.
114 (write-line "EHLO somehost" smtp)
115 (read-reply-code smtp) ;250
116 ;; Set sender email.
117 (write-line "MAIL FROM: <someone>" smtp)
118 (read-reply-code smtp) ;250
119 ;; Set recipient email.
120 (write-line "RCPT TO: <root>" smtp)
121 (read-reply-code smtp) ;250
122 ;; Send message.
123 (write-line "DATA" smtp)
124 (read-reply-code smtp) ;354
125 (write-line "Subject: Hello" smtp)
126 (newline smtp)
127 (write-line "Nice to meet you!" smtp)
128 (write-line "." smtp)
129 (read-reply-code smtp) ;250
130 ;; Say goodbye.
131 (write-line "QUIT" smtp)
132 (read-reply-code smtp) ;221
133 (close smtp)
134 #t))
135
136 (test-assert "mail arrived"
137 (marionette-eval
138 '(begin
139 (use-modules (ice-9 popen)
140 (ice-9 rdelim))
141
142 (define (queue-empty?)
c215d9ec
LC
143 (let* ((pipe (open-pipe* OPEN_READ
144 #$(file-append opensmtpd
145 "/sbin/smtpctl")
146 "show" "queue"))
147 (line (read-line pipe)))
148 (close-pipe pipe)
149 (eof-object? line)))
150
151 (let wait ((n 20))
152 (cond ((queue-empty?)
a37e03d6 153 (file-exists? "/var/spool/mail/root"))
c215d9ec
LC
154 ((zero? n)
155 (error "root mailbox didn't show up"))
156 (else
157 (sleep 1) (wait (- n 1))))))
8b113790
LC
158 marionette))
159
160 (test-end)
161 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
162
163 (gexp->derivation "opensmtpd-test" test))
a82f0b36
SB
164
165(define %test-opensmtpd
166 (system-test
167 (name "opensmtpd")
168 (description "Send an email to a running OpenSMTPD server.")
169 (value (run-opensmtpd-test))))
950026ac
CZ
170
171
172(define %exim-os
173 (simple-operating-system
39d7fdce 174 (service dhcp-client-service-type)
950026ac
CZ
175 (service mail-aliases-service-type '())
176 (service exim-service-type
177 (exim-configuration
178 (config-file
179 (plain-file "exim.conf" "
180primary_hostname = komputilo
181domainlist local_domains = @
182domainlist relay_to_domains =
183hostlist relay_from_hosts = localhost
184
185never_users =
186
187acl_smtp_rcpt = acl_check_rcpt
188acl_smtp_data = acl_check_data
189
190begin acl
191
192acl_check_rcpt:
193 accept
194acl_check_data:
195 accept
196"))))))
197
198(define (run-exim-test)
199 "Return a test of an OS running an Exim service."
8b113790
LC
200 (define vm
201 (virtual-machine
202 (operating-system (marionette-operating-system
203 %exim-os
204 #:imported-modules '((gnu services herd))))
205 (port-forwardings '((1025 . 25)))))
206
207 (define test
208 (with-imported-modules '((gnu build marionette)
209 (ice-9 ftw))
210 #~(begin
211 (use-modules (rnrs base)
212 (srfi srfi-64)
213 (ice-9 ftw)
214 (ice-9 rdelim)
215 (ice-9 regex)
216 (gnu build marionette))
217
218 (define marionette
219 (make-marionette '(#$vm)))
220
221 (define (read-reply-code port)
222 "Read a SMTP reply from PORT and return its reply code."
223 (let* ((line (read-line port))
224 (mo (string-match "([0-9]+)([ -]).*" line))
225 (code (string->number (match:substring mo 1)))
226 (finished? (string= " " (match:substring mo 2))))
227 (if finished?
228 code
229 (read-reply-code port))))
230
231 (define smtp (socket AF_INET SOCK_STREAM 0))
232 (define addr (make-socket-address AF_INET INADDR_LOOPBACK 1025))
233
234 (mkdir #$output)
235 (chdir #$output)
236
237 (test-begin "exim")
238
239 (test-assert "service is running"
240 (marionette-eval
241 '(begin
242 (use-modules (gnu services herd))
c24b1547 243 (start-service 'exim))
8b113790
LC
244 marionette))
245
246 (sleep 1) ;; give the service time to start talking
247
248 (connect smtp addr)
249 ;; Be greeted.
250 (test-eq "greeting received"
251 220 (read-reply-code smtp))
252 ;; Greet the server.
253 (write-line "EHLO somehost" smtp)
254 (test-eq "greeting successful"
255 250 (read-reply-code smtp))
256 ;; Set sender email.
257 (write-line "MAIL FROM: test@example.com" smtp)
258 (test-eq "sender set"
259 250 (read-reply-code smtp)) ;250
260 ;; Set recipient email.
261 (write-line "RCPT TO: root@komputilo" smtp)
262 (test-eq "recipient set"
263 250 (read-reply-code smtp)) ;250
264 ;; Send message.
265 (write-line "DATA" smtp)
266 (test-eq "data begun"
267 354 (read-reply-code smtp)) ;354
268 (write-line "Subject: Hello" smtp)
269 (newline smtp)
270 (write-line "Nice to meet you!" smtp)
271 (write-line "." smtp)
272 (test-eq "message sent"
273 250 (read-reply-code smtp)) ;250
274 ;; Say goodbye.
275 (write-line "QUIT" smtp)
276 (test-eq "quit successful"
277 221 (read-reply-code smtp)) ;221
278 (close smtp)
279
280 (test-eq "the email is received"
281 1
282 (marionette-eval
283 '(begin
284 (use-modules (ice-9 ftw))
285 (length (scandir "/var/spool/exim/msglog"
286 (lambda (x) (not (string-prefix? "." x))))))
287 marionette))
288
289 (test-end)
290 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
291
292 (gexp->derivation "exim-test" test))
950026ac
CZ
293
294(define %test-exim
295 (system-test
296 (name "exim")
297 (description "Send an email to a running an Exim server.")
298 (value (run-exim-test))))
a9079b48
OP
299
300(define %dovecot-os
301 (simple-operating-system
39d7fdce 302 (service dhcp-client-service-type)
a9079b48
OP
303 (dovecot-service #:config
304 (dovecot-configuration
305 (disable-plaintext-auth? #f)
306 (ssl? "no")
307 (auth-mechanisms '("anonymous"))
308 (auth-anonymous-username "alice")
309 (mail-location
310 (string-append "maildir:~/Maildir"
311 ":INBOX=~/Maildir/INBOX"
312 ":LAYOUT=fs"))))))
313
314(define (run-dovecot-test)
315 "Return a test of an OS running Dovecot service."
316 (define vm
317 (virtual-machine
318 (operating-system (marionette-operating-system
319 %dovecot-os
320 #:imported-modules '((gnu services herd))))
321 (port-forwardings '((8143 . 143)))))
322
323 (define test
324 (with-imported-modules '((gnu build marionette))
325 #~(begin
326 (use-modules (gnu build marionette)
327 (ice-9 iconv)
328 (ice-9 rdelim)
329 (rnrs base)
330 (rnrs bytevectors)
331 (srfi srfi-64))
332
333 (define marionette
334 (make-marionette '(#$vm)))
335
336 (define* (message-length message #:key (encoding "iso-8859-1"))
337 (bytevector-length (string->bytevector message encoding)))
338
339 (define message "From: test@example.com\n\
340Subject: Hello Nice to meet you!")
341
342 (mkdir #$output)
343 (chdir #$output)
344
345 (test-begin "dovecot")
346
347 ;; Wait for dovecot to be up and running.
c24b1547 348 (test-assert "dovecot running"
a9079b48
OP
349 (marionette-eval
350 '(begin
351 (use-modules (gnu services herd))
c24b1547 352 (start-service 'dovecot))
a9079b48
OP
353 marionette))
354
355 ;; Check Dovecot service's PID.
356 (test-assert "service process id"
357 (let ((pid
358 (number->string (wait-for-file "/var/run/dovecot/master.pid"
359 marionette))))
360 (marionette-eval `(file-exists? (string-append "/proc/" ,pid))
361 marionette)))
362
363 (test-assert "accept an email"
364 (let ((imap (socket AF_INET SOCK_STREAM 0))
365 (addr (make-socket-address AF_INET INADDR_LOOPBACK 8143)))
366 (connect imap addr)
367 ;; Be greeted.
368 (read-line imap) ;OK
369 ;; Authenticate
370 (write-line "a AUTHENTICATE ANONYMOUS" imap)
371 (read-line imap) ;+
372 (write-line "c2lyaGM=" imap)
373 (read-line imap) ;OK
374 ;; Create a TESTBOX mailbox
375 (write-line "a CREATE TESTBOX" imap)
376 (read-line imap) ;OK
377 ;; Append a message to a TESTBOX mailbox
378 (write-line (format #f "a APPEND TESTBOX {~a}"
379 (number->string (message-length message)))
380 imap)
381 (read-line imap) ;+
382 (write-line message imap)
383 (read-line imap) ;OK
384 ;; Logout
385 (write-line "a LOGOUT" imap)
386 (close imap)
387 #t))
388
389 (test-equal "mail arrived"
390 message
391 (marionette-eval
392 '(begin
393 (use-modules (ice-9 ftw)
394 (ice-9 match))
395 (let ((TESTBOX/new "/home/alice/Maildir/TESTBOX/new/"))
396 (match (scandir TESTBOX/new)
397 (("." ".." message-file)
398 (call-with-input-file
399 (string-append TESTBOX/new message-file)
400 get-string-all)))))
401 marionette))
402
403 (test-end)
404 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
405
406 (gexp->derivation "dovecot-test" test))
407
408(define %test-dovecot
409 (system-test
410 (name "dovecot")
411 (description "Connect to a running Dovecot server.")
412 (value (run-dovecot-test))))
f6b0e1f8
CB
413
414(define %getmail-os
827abd5d
LC
415 (operating-system
416 (inherit (simple-operating-system))
417
418 ;; Set a password for the user account; the test needs it.
419 (users (cons (user-account
420 (name "alice")
421 (password (crypt "testpass" "$6$abc"))
422 (comment "Bob's sister")
423 (group "users")
424 (supplementary-groups '("wheel" "audio" "video")))
425 %base-user-accounts))
426
427 (services (cons* (service dhcp-client-service-type)
428 (service dovecot-service-type
429 (dovecot-configuration
430 (disable-plaintext-auth? #f)
431 (ssl? "no")
432 (auth-mechanisms '("anonymous" "plain"))
433 (auth-anonymous-username "alice")
434 (mail-location
435 (string-append "maildir:~/Maildir"
436 ":INBOX=~/Maildir/INBOX"
437 ":LAYOUT=fs"))))
438 (service getmail-service-type
439 (list
440 (getmail-configuration
441 (name 'test)
442 (user "alice")
443 (directory "/var/lib/getmail/alice")
444 (idle '("TESTBOX"))
445 (rcfile
446 (getmail-configuration-file
447 (retriever
448 (getmail-retriever-configuration
449 (type "SimpleIMAPRetriever")
450 (server "localhost")
451 (username "alice")
452 (port 143)
453 (extra-parameters
454 '((password . "testpass")
455 (mailboxes . ("TESTBOX"))))))
456 (destination
457 (getmail-destination-configuration
458 (type "Maildir")
459 (path "/home/alice/TestMaildir/")))
460 (options
461 (getmail-options-configuration
462 (read-all #f))))))))
463 %base-services))))
f6b0e1f8
CB
464
465(define (run-getmail-test)
466 "Return a test of an OS running Getmail service."
467 (define vm
468 (virtual-machine
469 (operating-system (marionette-operating-system
470 %getmail-os
471 #:imported-modules '((gnu services herd))))
472 (port-forwardings '((8143 . 143)))))
473
474 (define test
475 (with-imported-modules '((gnu build marionette))
476 #~(begin
477 (use-modules (gnu build marionette)
478 (ice-9 iconv)
479 (ice-9 rdelim)
480 (rnrs base)
481 (rnrs bytevectors)
482 (srfi srfi-64))
483
484 (define marionette
485 (make-marionette '(#$vm)))
486
487 (define* (message-length message #:key (encoding "iso-8859-1"))
488 (bytevector-length (string->bytevector message encoding)))
489
490 (define message "From: test@example.com\n\
491Subject: Hello Nice to meet you!")
492
493 (mkdir #$output)
494 (chdir #$output)
495
496 (test-begin "getmail")
497
498 ;; Wait for dovecot to be up and running.
499 (test-assert "dovecot running"
500 (marionette-eval
501 '(begin
502 (use-modules (gnu services herd))
503 (start-service 'dovecot))
504 marionette))
505
f6b0e1f8
CB
506 ;; Wait for getmail to be up and running.
507 (test-assert "getmail-test running"
508 (marionette-eval
509 '(let* ((pw (getpw "alice"))
510 (uid (passwd:uid pw))
511 (gid (passwd:gid pw)))
512 (use-modules (gnu services herd))
513
514 (for-each
515 (lambda (dir)
516 (mkdir dir)
517 (chown dir uid gid))
518 '("/home/alice/TestMaildir"
519 "/home/alice/TestMaildir/cur"
520 "/home/alice/TestMaildir/new"
521 "/home/alice/TestMaildir/tmp"
522 "/home/alice/TestMaildir/TESTBOX"
523 "/home/alice/TestMaildir/TESTBOX/cur"
524 "/home/alice/TestMaildir/TESTBOX/new"
525 "/home/alice/TestMaildir/TESTBOX/tmp"))
526
527 (start-service 'getmail-test))
528 marionette))
529
530 ;; Check Dovecot service's PID.
531 (test-assert "service process id"
532 (let ((pid
533 (number->string (wait-for-file "/var/run/dovecot/master.pid"
534 marionette))))
535 (marionette-eval `(file-exists? (string-append "/proc/" ,pid))
536 marionette)))
537
538 (test-assert "accept an email"
539 (let ((imap (socket AF_INET SOCK_STREAM 0))
540 (addr (make-socket-address AF_INET INADDR_LOOPBACK 8143)))
541 (connect imap addr)
542 ;; Be greeted.
543 (read-line imap) ;OK
544 ;; Authenticate
545 (write-line "a AUTHENTICATE ANONYMOUS" imap)
546 (read-line imap) ;+
547 (write-line "c2lyaGM=" imap)
548 (read-line imap) ;OK
549 ;; Create a TESTBOX mailbox
550 (write-line "a CREATE TESTBOX" imap)
551 (read-line imap) ;OK
552 ;; Append a message to a TESTBOX mailbox
553 (write-line (format #f "a APPEND TESTBOX {~a}"
554 (number->string (message-length message)))
555 imap)
556 (read-line imap) ;+
557 (write-line message imap)
558 (read-line imap) ;OK
559 ;; Logout
560 (write-line "a LOGOUT" imap)
561 (close imap)
562 #t))
563
564 (sleep 1)
565
566 (test-assert "mail arrived"
567 (string-contains
568 (marionette-eval
569 '(begin
570 (use-modules (ice-9 ftw)
571 (ice-9 match))
572 (let ((TESTBOX/new "/home/alice/TestMaildir/new/"))
573 (match (scandir TESTBOX/new)
574 (("." ".." message-file)
575 (call-with-input-file
576 (string-append TESTBOX/new message-file)
577 get-string-all)))))
578 marionette)
579 message))
580
581 (test-end)
582 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
583
584 (gexp->derivation "getmail-test" test))
585
586(define %test-getmail
587 (system-test
588 (name "getmail")
589 (description "Connect to a running Getmail server.")
590 (value (run-getmail-test))))