git.fiddlerwoaroof.com
Raw Blame History
;; See the file LICENSE for the full license governing this code.

;; imap testing
;; requires smtp module too

(eval-when (compile load eval)
  (require :rfc2822)
  (require :smtp)
  (require :imap)
  (require :test))


(in-package :test)


(defparameter *test-machine* "tiger.franz.com")
(defparameter *test-account* "jkfmail")
(defparameter *test-password* "jkf.imap")


(defparameter *test-email* (format nil "~a@~a" *test-account* *test-machine*))


(defun test-connect ()
  ;; test connecting and disconnecting from the server
  
  (let ((mb (net.post-office:make-imap-connection *test-machine*
				     :user *test-account*
				     :password *test-password*)))
    (unwind-protect
	(progn
    
	  (test-t (not (null mb)))  ; make sure we got a mailbox object
    
	  ; check that we've stored resonable values in the mb object
	  (test-equal "/" (net.post-office:mailbox-separator mb)) 
    
	  (test-t (net.post-office::select-mailbox mb "inbox"))
    
	  (test-t (> (net.post-office:mailbox-uidvalidity mb) 0))
	  (test-t (not (null (net.post-office:mailbox-flags mb)))))
    
      (test-t (net.post-office:close-connection mb)))))


(defun test-sends ()
  ;; test sending and reading mail
  (let ((mb (net.post-office:make-imap-connection *test-machine*
				     :user *test-account*
				     :password *test-password*)))
    (unwind-protect
	(progn
	  (test-t (not (null mb)))  ; make sure we got a mailbox object

	  ;; go through the mailboxes and delete all letters
	  (dolist (mblist (net.post-office:mailbox-list mb :pattern "*"))
	    (if* (not (member :\\noselect (net.post-office:mailbox-list-flags mblist)))
	       then (net.post-office:select-mailbox mb (net.post-office:mailbox-list-name mblist))
		    (let ((count (net.post-office:mailbox-message-count mb)))
		      ; remove all old mail
		      (if* (> count 0)
			 then (net.post-office:alter-flags mb `(:seq 1 ,count) :add-flags :\\deleted)
			      (net.post-office:expunge-mailbox mb)
			      (test-eql 0 (net.post-office:mailbox-message-count mb)))
		      ; remove mailbox (except inbox)
		      (if* (not (equalp "inbox" (net.post-office:mailbox-list-name mblist)))
			 then ; must not be selected if we want to del
			      (net.post-office:select-mailbox mb "inbox") 
			      (net.post-office:delete-mailbox mb (net.post-office:mailbox-list-name mblist)))
      
		      )))
      
    
	  ;; send five letters
	  (dotimes (i 5)
	    (net.post-office:send-smtp *test-machine*
			    *test-email*
			    *test-email*
			    (format nil "message number ~d" (1+ i))))
    
	  ; test to see if imap figures out that the letters are there
	  (net.post-office:select-mailbox mb "inbox")

	  ; wait a bit for the mail to be delivered
	  (dotimes (i 5) 
	    (if* (not (eql 5 (net.post-office:mailbox-message-count mb)))
	       then (sleep 1)
		    (net.post-office: noop mb)))
	      
	  (test-eql 5 (net.post-office:mailbox-message-count mb))
    
	  ; test the search facility
	  ; look for the message number we put in each message.
	  ; I hope the letters get delivered in order...
	  (dotimes (i 5)
	    (let ((mn (1+ i)))
	      (test-equal (list mn)
			  (net.post-office:search-mailbox mb 
					     `(:body ,(format nil "~d" mn))))))
	  
	  ; test getting data from mail message
	  (let ((fetch-info (net.post-office:fetch-parts mb 
					   1
					   "(envelope body[1])")))
	    (let ((envelope (net.post-office:fetch-field 1 "envelope" fetch-info))
		  (body (net.post-office:fetch-field 1 "body[1]" fetch-info)))
	      (test-equal "jkfmail" (net.post-office:address-mailbox
				     (car (net.post-office:envelope-from envelope))))
	      (test-nil (net.post-office:address-mailbox
			 (car (net.post-office:envelope-to envelope))))
	      
	      (test-equal (format nil "message number 1~c" #\newline)
			  body))))
      (test-t (net.post-office:close-connection mb)))))
    
    

(defun test-flags ()
  ;; test setting and getting flags
  ;;
  ;; assume we have 5 messages in inbox at this time
  ;;
  (let ((mb (net.post-office:make-imap-connection *test-machine*
				     :user *test-account*
				     :password *test-password*)))
    (unwind-protect
	(progn
	  (net.post-office:select-mailbox mb "inbox")
	  
	  (let ((flags (net.post-office:fetch-field 3 
				       "flags"
				       (net.post-office:fetch-parts 
					mb 3 "flags"))))
	    (test-nil flags))
				       
	  ;; add flags
	  (let ((info (net.post-office:alter-flags mb 3 :add-flags :\\deleted)))
	    (test-equal '(:\\deleted)
			(net.post-office:fetch-field 3 "flags" info)))

	  ; good bye message
	  (test-equal '(3) (net.post-office:expunge-mailbox mb))
	  
	  (net.post-office:alter-flags mb 4 :add-flags ':\\bbbb)
	  (test-equal '(:\\bbbb)
		      (net.post-office:fetch-field 4 "flags"
				      (net.post-office:fetch-parts mb 4
						       "flags")))
	  
	  
	  )
      (test-t (net.post-office:close-connection mb)))))

(defun test-mailboxes ()
  ;; should be 4 messages now in inbox
  ;; let's create 4 mailboxes, one for each letter
  (let ((mb (net.post-office:make-imap-connection *test-machine*
				     :user *test-account*
				     :password *test-password*)))
    (unwind-protect
	(progn 
	  (net.post-office:select-mailbox mb "inbox")
	  (dotimes (i 4)
	    (let ((mbname (format nil "temp/mb~d" i)))
	      (test-t (net.post-office:create-mailbox mb mbname))
	      (net.post-office:copy-to-mailbox mb (1+ i) mbname)))
	  
	  ; now check that each new mailbox has one message
	  (dotimes (i 4)
	    (let ((mbname (format nil "temp/mb~d" i)))
	      (net.post-office:select-mailbox mb mbname)
	      (test-eql 1 (net.post-office:mailbox-message-count mb)))))
      (test-t (net.post-office:close-connection mb)))))


(defun test-pop ()
  ;; test out the pop interface to the mailbox.
  
  (let ((pb (net.post-office:make-pop-connection *test-machine*
				    :user *test-account*
				    :password *test-password*)))
    ; still from before
    (test-eql 4 (net.post-office:mailbox-message-count pb))
    
    (test-eql 4 (length (net.post-office:unique-id pb)))
			 
    (net.post-office:delete-letter pb '(:seq 2 3))
    
    (test-eql 2 (length (net.post-office:unique-id pb)))
    
    (test-eql 4 (and :second (net.post-office:mailbox-message-count pb)))
    
    (net.post-office:noop pb)
    
    (test-eql 2 (and :third (net.post-office:mailbox-message-count pb)))
    
    (net.post-office:fetch-letter pb 1)
    (test-err (net.post-office:fetch-letter pb 2))
    (test-err (net.post-office:fetch-letter pb 3))
    (net.post-office:fetch-letter pb 4)
    
    (net.post-office:close-connection pb)
    
    (setq pb (net.post-office:make-pop-connection *test-machine*
				    :user *test-account*
				    :password *test-password*))
    
    (test-eql 2 (and :fourth (net.post-office:mailbox-message-count pb)))
    
    (net.post-office:fetch-letter pb 1) ; just make sure there's no error
    
    (net.post-office:top-lines pb 1 1)  ; just make sure there's no error
    (net.post-office:make-envelope-from-text (net.post-office:top-lines pb 1 0))
    
    (net.post-office:close-connection pb)))


(defun test-mime ()
  (test-equal
   "foobar baz"
   (net.post-office:decode-header-text "=?utf-8?q?foo?=
  =?utf-8?q?bar?= baz"))
  (test-equal
   "before brucejones hello"
   (net.post-office:decode-header-text "before =?utf-8?q?bruce?=    =?utf-8?q?jones?= hello"))
  (test-equal
   "[Franz Wiki] Update of \"Office/EmployeeDirectory\" by SteveHaflich"
   (net.post-office:decode-header-text "=?utf-8?q?=5BFranz_Wiki=5D_Update_of_=22Office/EmployeeDirectory=22_by_St?=
 =?utf-8?q?eveHaflich?="))
  )

(defun test-parse-email-address ()
  (dolist (good `(("foo@bar.com" "foo" "bar.com")
		  ("layer@franz.com" "layer" "franz.com")
		  ("

layer@franz.com" "layer" "franz.com")
		  (,(replace-re "XXlayer@franz.comX  X"
				"X"
				(format nil "~c" #\newline)
				:single-line t)
		   "layer" "franz.com")
		  (,(replace-re "XXlayer@franz.comX  X"
				"X"
				(format nil "~c" #\return)
				:single-line t)
		   "layer" "franz.com")
		  ;; local-part length = 64
		  ("1234567890123456789012345678901234567890123456789012345678901234@foo.com"
		   "1234567890123456789012345678901234567890123456789012345678901234"
		   "foo.com")
		  ))
    (multiple-value-bind (local-part domain)
	(net.mail:parse-email-address (first good))
      (test-equal (second good) local-part)
      (test-equal (third good) domain)))
  (dolist (bad (list "@foo.com"
		     ;; local-part length = 65
		     "12345678901234567890123456789012345678901234567890123456789012345@foo.com"
		     ))
    (test-nil (net.mail:parse-email-address bad)))
  )
	  
    
(defun test-imap ()
  (handler-bind ((net.post-office:po-condition 
		  #'(lambda (con)
		      (format t "Got imap condition: ~a~%" con))))
    (test-mime)
    (test-parse-email-address)
;;;; Only jkf is setup to run the tests.
    (when (string= "jkf" (sys:getenv "USER"))
      (test-connect)
      (test-sends)
      (test-flags)
      (test-mailboxes)
      (test-pop)
      )))


(if* *do-test* then (do-test :imap #'test-imap))