Browse Source

ZF-10741: adding unit tests for Zend_Mail_Protocol_Smtp

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@23475 44c647ce-9c0f-0410-b52a-842ac1e357ba
mjh_ca 15 years ago
parent
commit
2849747090

+ 14 - 3
library/Zend/Mail/Protocol/Abstract.php

@@ -277,7 +277,7 @@ abstract class Zend_Mail_Protocol_Abstract
             throw new Zend_Mail_Protocol_Exception($errorStr);
         }
 
-        if (($result = stream_set_timeout($this->_socket, self::TIMEOUT_CONNECTION)) === false) {
+        if (($result = $this->_setStreamTimeout(self::TIMEOUT_CONNECTION)) === false) {
             /**
              * @see Zend_Mail_Protocol_Exception
              */
@@ -357,7 +357,7 @@ abstract class Zend_Mail_Protocol_Abstract
 
         // Adapters may wish to supply per-commend timeouts according to appropriate RFC
         if ($timeout !== null) {
-           stream_set_timeout($this->_socket, $timeout);
+            $this->_setStreamTimeout($timeout);
         }
 
         // Retrieve response
@@ -428,9 +428,20 @@ abstract class Zend_Mail_Protocol_Abstract
              * @see Zend_Mail_Protocol_Exception
              */
             require_once 'Zend/Mail/Protocol/Exception.php';
-            throw new Zend_Mail_Protocol_Exception($errMsg);
+            throw new Zend_Mail_Protocol_Exception($errMsg, $cmd);
         }
 
         return $msg;
     }
+
+    /**
+     * Set stream timeout
+     *
+     * @param integer $timeout
+     * @return boolean
+     */
+    protected function _setStreamTimeout($timeout)
+    {
+       return stream_set_timeout($this->_socket, $timeout);
+    }
 }

+ 3 - 3
library/Zend/Mail/Protocol/Smtp.php

@@ -327,7 +327,7 @@ class Zend_Mail_Protocol_Smtp extends Zend_Mail_Protocol_Abstract
 
 
     /**
-     * Issues the RSET command end validates answer
+     * Issues the RSET command and validates answer
      *
      * Can be used to restore a clean smtp communication state when a transaction has been cancelled or commencing a new transaction.
      *
@@ -346,7 +346,7 @@ class Zend_Mail_Protocol_Smtp extends Zend_Mail_Protocol_Abstract
 
 
     /**
-     * Issues the NOOP command end validates answer
+     * Issues the NOOP command and validates answer
      *
      * Not used by Zend_Mail, could be used to keep a connection alive or check if it is still open.
      *
@@ -360,7 +360,7 @@ class Zend_Mail_Protocol_Smtp extends Zend_Mail_Protocol_Abstract
 
 
     /**
-     * Issues the VRFY command end validates answer
+     * Issues the VRFY command and validates answer
      *
      * Not used by Zend_Mail.
      *

+ 2 - 0
tests/Zend/Mail/AllTests.php

@@ -39,6 +39,7 @@ require_once 'Zend/Mail/ImapTest.php';
 require_once 'Zend/Mail/InterfaceTest.php';
 require_once 'Zend/Mail/MessageTest.php';
 require_once 'Zend/Mail/SmtpOfflineTest.php';
+require_once 'Zend/Mail/SmtpProtocolTest.php';
 require_once 'Zend/Mail/SmtpTest.php';
 require_once 'Zend/Mail/FileTransportTest.php';
 
@@ -80,6 +81,7 @@ class Zend_Mail_AllTests
             $suite->addTestSuite('Zend_Mail_MaildirWritableTest');
         }
 	$suite->addTestSuite('Zend_Mail_SmtpOfflineTest');
+	$suite->addTestSuite('Zend_Mail_SmtpProtocolTest');
         if (defined('TESTS_ZEND_MAIL_SMTP_ENABLED') && constant('TESTS_ZEND_MAIL_SMTP_ENABLED') == true) {
             $suite->addTestSuite('Zend_Mail_SmtpTest');
         }

+ 374 - 0
tests/Zend/Mail/SmtpProtocolTest.php

@@ -0,0 +1,374 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** 
+ * Zend_Mail
+ */
+require_once 'Zend/Mail.php';
+
+/**
+ * Zend_Mail_Protocol_Smtp
+ */
+require_once 'Zend/Mail/Protocol/Smtp.php';
+
+
+/**
+ * PHPUnit test case
+ */
+require_once 'PHPUnit/Framework/TestCase.php';
+
+/**
+ * Test helper for configuration when run standalone
+ */
+require_once dirname(__FILE__) . '/../../TestHelper.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Mail
+ */
+class Zend_Mail_SmtpProtocolTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * @var Zend_Mail_Protocol_Smtp
+     */
+    protected $_protocol;
+
+    public function setUp()
+    {
+        $this->_protocol = new ProtocolMock();
+    }
+
+    public function testEhlo()
+    {
+        $this->_connectAndEhlo(); // expects 250 response
+
+        $this->assertEquals(array(
+            '220 example.com ESMTP welcome',
+            'EHLO 127.0.0.1',
+            '250 Hello 127.0.0.1, go ahead'
+        ), $this->_protocol->dialog);
+    }
+
+    /**
+     * @depends testEhlo
+     * @expectedException Zend_Mail_Protocol_Exception
+     */
+    public function testHeloIsOnlyAllowedOncePerSession()
+    {
+        $this->_connectAndEhlo(); // do it once
+        $this->_protocol->helo(); // do it again
+    }
+
+    /**
+     * @depends testEhlo
+     */
+    public function testEhloFallsBackToHelo()
+    {
+        $this->_protocol->responseBuffer = array(
+            '220 example.com ESMTP welcome',
+            '500 Unrecognized', /* 500 or 502 error on unrecognized EHLO */
+            '250 Hello 127.0.0.1, go ahead'
+        );
+
+        $this->_protocol->connect();
+        $this->_protocol->helo();
+
+        $this->assertEquals(array(
+            '220 example.com ESMTP welcome',
+            'EHLO 127.0.0.1', // tries EHLO
+            '500 Unrecognized', // .. which fails
+            'HELO 127.0.0.1', // continues to HELO
+            '250 Hello 127.0.0.1, go ahead' // success
+        ), $this->_protocol->dialog);
+    }
+
+    /**
+     * @depends testEhlo
+     */
+    public function testMail()
+    {
+        $p = $this->_protocol;
+        $expectedDialog = $this->_connectAndEhlo();
+
+        $expectedDialog[] = 'MAIL FROM:<from@example.com>';
+        $expectedDialog[] = $p->responseBuffer[] = '250 Sender accepted';
+        $this->_protocol->mail('from@example.com');
+
+        $this->assertEquals($expectedDialog, $this->_protocol->dialog);
+    }
+
+    /**
+     * @depends testMail
+     */
+    public function testRcptExpects250()
+    {
+        $p = $this->_protocol;
+        $expectedDialog = $this->_connectAndEhlo();
+
+        $expectedDialog[] = 'MAIL FROM:<from@example.com>';
+        $expectedDialog[] = $p->responseBuffer[] = '250 Sender accepted';
+        $this->_protocol->mail('from@example.com');
+
+        $expectedDialog[] = 'RCPT TO:<to@example.com>';
+        $expectedDialog[] = $p->responseBuffer[] = '250 Recipient OK';
+        $this->_protocol->rcpt('to@example.com');
+
+        $this->assertEquals($expectedDialog, $this->_protocol->dialog);
+    }
+
+    /**
+     * @depends testMail
+     */
+    public function testRcptExpects251()
+    {
+        $p = $this->_protocol;
+        $expectedDialog = $this->_connectAndEhlo();
+
+        $expectedDialog[] = 'MAIL FROM:<from@example.com>';
+        $expectedDialog[] = $p->responseBuffer[] = '250 Sender accepted';
+        $this->_protocol->mail('from@example.com');
+
+        $expectedDialog[] = 'RCPT TO:<to@example.com>';
+        $expectedDialog[] = $p->responseBuffer[] = '251 Recipient OK';
+        $this->_protocol->rcpt('to@example.com');
+
+        $this->assertEquals($expectedDialog, $this->_protocol->dialog);
+    }
+
+
+    /**
+     * @depends testRcptExpects250
+     */
+    public function testData()
+    {
+        $p = $this->_protocol;
+        $expectedDialog = $this->_connectAndEhlo();
+
+        $expectedDialog[] = 'MAIL FROM:<from@example.com>';
+        $expectedDialog[] = $p->responseBuffer[] = '250 Sender accepted';
+        $this->_protocol->mail('from@example.com');
+
+        $expectedDialog[] = 'RCPT TO:<to@example.com>';
+        $expectedDialog[] = $p->responseBuffer[] = '250 Recipient OK';
+        $this->_protocol->rcpt('to@example.com');
+
+        $expectedDialog[] = 'DATA';
+        $expectedDialog[] = $p->responseBuffer[] = '354 Go ahead';
+        $expectedDialog[] = 'foo';
+        $expectedDialog[] = '.'; // end of data marker
+        $expectedDialog[] = $p->responseBuffer[] = '250 Accepted';
+        $this->_protocol->data('foo');
+
+        $this->assertEquals($expectedDialog, $this->_protocol->dialog);
+    }
+
+    /**
+     * @depends testEhlo
+     */
+    public function testRset()
+    {
+        $expectedDialog = $this->_connectAndEhlo();
+
+        $this->_protocol->responseBuffer = array('250 OK');
+        $expectedDialog[] = 'RSET';
+        $expectedDialog[] = '250 OK';
+
+        $this->_protocol->rset();
+
+        $this->assertEquals($expectedDialog, $this->_protocol->dialog);
+    }
+
+    /**
+     * @depends testEhlo
+     * @group ZF-1377
+     */
+    public function testRsetExpects220()
+    {
+        $expectedDialog = $this->_connectAndEhlo();
+
+        // Microsoft ESMTP server responds to RSET with 220 rather than 250
+        $this->_protocol->responseBuffer = array('220 OK');
+        $expectedDialog[] = 'RSET';
+        $expectedDialog[] = '220 OK';
+
+        $this->_protocol->rset();
+
+        $this->assertEquals($expectedDialog, $this->_protocol->dialog);
+    }
+
+    /**
+     * @depends testEhlo
+     */
+    public function testQuit()
+    {
+        $p = $this->_protocol;
+        $expectedDialog = $this->_connectAndEhlo();
+
+        $expectedDialog[] = 'QUIT';
+        $expectedDialog[] = $p->responseBuffer[] = '221 goodbye';
+        
+        $this->_protocol->quit();
+        
+        $this->assertEquals($expectedDialog, $this->_protocol->dialog);
+    }
+
+    /**
+     * @depends testMail
+     * @group ZF-8511
+     */
+    public function testMultilineResponsesAreNotTruncated()
+    {
+        $this->_connectAndEhlo();
+
+        $this->_protocol->responseBuffer[] = '550-line one';
+        $this->_protocol->responseBuffer[] = '550 line two';
+
+        try {
+            $this->_protocol->mail('from@example.com');
+            $this->fail('Expected exception on 550 response');
+        } catch (Zend_Mail_Protocol_Exception $e) {
+            $this->assertEquals('line one line two', $e->getMessage());
+        }
+    }
+
+    /**
+     * @depends testMail
+     * @group ZF-10249
+     */
+    public function testExceptionCodeIsSmtpStatusCode()
+    {
+        $p = $this->_protocol;
+        $this->_connectAndEhlo();
+
+        $p->responseBuffer[] = '550 failure';
+
+        try {
+            $this->_protocol->mail('from@example.com');
+            $this->fail('Expected exception on 550 response');
+        } catch (Zend_Mail_Protocol_Exception $e) {
+            $this->assertEquals(550, $e->getCode());
+        }
+    }
+
+    /**
+     * @depends testMail
+     * @expectedException Zend_Mail_Protocol_Exception
+     */
+    public function testRcptThrowsExceptionOnUnexpectedResponse()
+    {
+        $p = $this->_protocol;
+        $expectedDialog = $this->_connectAndEhlo();
+
+        $expectedDialog[] = 'MAIL FROM:<from@example.com>';
+        $expectedDialog[] = $p->responseBuffer[] = '250 Sender accepted';
+        $this->_protocol->mail('from@example.com');
+
+        $expectedDialog[] = 'RCPT TO:<to@example.com>';
+        $expectedDialog[] = $p->responseBuffer[] = '500 error';
+        $this->_protocol->rcpt('to@example.com');
+    }
+
+
+    public function testMailBeforeHeloThrowsException()
+    {
+        try {
+            $this->_protocol->mail('from@example.com');
+            $this->fail('mail() before helo() should throw exception');
+        } catch (Zend_Mail_Protocol_Exception $e) {
+            $this->assertEquals('A valid session has not been started', $e->getMessage());
+        }
+    }
+
+    /**
+     * @depends testEhlo
+     */
+    public function testRcptBeforeMailThrowsException()
+    {
+        $this->_connectAndEhlo();
+
+        try {
+            $this->_protocol->rcpt('to@example.com');
+            $this->fail('rcpt() before mail() should throw exception');
+        } catch (Zend_Mail_Protocol_Exception $e) {
+            $this->assertEquals('No sender reverse path has been supplied', $e->getMessage());
+        }
+    }
+
+    /**
+     * @depends testEhlo
+     * @expectedException Zend_Mail_Protocol_Exception
+     */
+    public function testDataBeforeRcptThrowsException()
+    {
+        $this->_connectAndEhlo();
+
+        $this->_protocol->data('foo');
+    }
+
+    /**
+     * Performs the initial EHLO dialog
+     */
+    protected function _connectAndEhlo()
+    {
+        $this->_protocol->responseBuffer = array(
+            '220 example.com ESMTP welcome',
+            '250 Hello 127.0.0.1, go ahead'
+        );
+
+        $this->_protocol->connect();
+        $this->_protocol->helo();
+        return $this->_protocol->dialog;
+    }
+}
+
+
+class ProtocolMock extends Zend_Mail_Protocol_Smtp
+{
+    public $dialog = array();
+    public $responseBuffer = array();
+
+    /**
+     * Override connect function to use local file for testing
+     *
+     * @param string $remote
+     */
+    protected function _connect($remote)
+    {
+        $this->_socket = tmpfile();
+    }
+    
+    protected function _send($request)
+    {
+        $this->dialog[] = $request;
+    }
+
+    protected function _receive($timeout = null)
+    {
+        $line = array_shift($this->responseBuffer);
+        $this->dialog[] = $line;
+        return $line;
+    }
+}