Skip to content

Commit

Permalink
Live mailbox testing support added
Browse files Browse the repository at this point in the history
  • Loading branch information
Webklex committed Mar 6, 2023
1 parent ec97dc5 commit f1e3f83
Show file tree
Hide file tree
Showing 9 changed files with 345 additions and 25 deletions.
23 changes: 23 additions & 0 deletions .github/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM ubuntu:latest
LABEL maintainer="Webklex <github@webklex.com>"

RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y sudo dovecot-imapd

ENV LIVE_MAILBOX=true
ENV LIVE_MAILBOX_HOST=mail.example.local
ENV LIVE_MAILBOX_PORT=993
ENV LIVE_MAILBOX_ENCRYPTION=ssl
ENV LIVE_MAILBOX_VALIDATE_CERT=true
ENV LIVE_MAILBOX_USERNAME=root@example.local
ENV LIVE_MAILBOX_PASSWORD=foobar
ENV LIVE_MAILBOX_QUOTA_SUPPORT=true

EXPOSE 993

ADD dovecot_setup.sh /root/dovecot_setup.sh
RUN chmod +x /root/dovecot_setup.sh

CMD ["/bin/bash", "-c", "/root/dovecot_setup.sh && tail -f /dev/null"]

63 changes: 63 additions & 0 deletions .github/docker/dovecot_setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/sh

set -ex

sudo apt-get -q update
sudo apt-get -q -y install dovecot-imapd

{
echo "127.0.0.1 $LIVE_MAILBOX_HOST"
} | sudo tee -a /etc/hosts

SSL_CERT="/etc/ssl/certs/dovecot.crt"
SSL_KEY="/etc/ssl/private/dovecot.key"

sudo openssl req -new -x509 -days 3 -nodes \
-out "$SSL_CERT" \
-keyout "$SSL_KEY" \
-subj "/C=EU/ST=Europe/L=Home/O=Webklex/OU=Webklex DEV/CN=""$LIVE_MAILBOX_HOST"

sudo chown root:dovecot "$SSL_CERT" "$SSL_KEY"
sudo chmod 0440 "$SSL_CERT"
sudo chmod 0400 "$SSL_KEY"

DOVECOT_CONF="/etc/dovecot/local.conf"
MAIL_CONF="/etc/dovecot/conf.d/10-mail.conf"
IMAP_CONF="/etc/dovecot/conf.d/20-imap.conf"
QUOTA_CONF="/etc/dovecot/conf.d/90-quota.conf"
sudo touch "$DOVECOT_CONF" "$MAIL_CONF" "$IMAP_CONF" "$QUOTA_CONF"
sudo chown root:dovecot "$DOVECOT_CONF" "$MAIL_CONF" "$IMAP_CONF" "$QUOTA_CONF"
sudo chmod 0640 "$DOVECOT_CONF" "$MAIL_CONF" "$IMAP_CONF" "$QUOTA_CONF"

{
echo "ssl = required"
echo "disable_plaintext_auth = yes"
echo "ssl_cert = <""$SSL_CERT"
echo "ssl_key = <""$SSL_KEY"
echo "ssl_protocols = !SSLv2 !SSLv3"
echo "ssl_cipher_list = AES128+EECDH:AES128+EDH"
} | sudo tee -a "$DOVECOT_CONF"

{
echo "mail_plugins = \$mail_plugins quota"
} | sudo tee -a "$MAIL_CONF"

{
echo "protocol imap {"
echo " mail_plugins = \$mail_plugins imap_quota"
echo "}"
} | sudo tee -a "$IMAP_CONF"

{
echo "plugin {"
echo " quota = maildir:User quota"
echo " quota_rule = *:storage=1G"
echo "}"
} | sudo tee -a "$QUOTA_CONF"

sudo useradd --create-home --shell /bin/false "$LIVE_MAILBOX_USERNAME"
echo "$LIVE_MAILBOX_USERNAME"":""$LIVE_MAILBOX_PASSWORD" | sudo chpasswd

sudo service dovecot restart

sudo doveadm auth test -x service=imap "$LIVE_MAILBOX_USERNAME" "$LIVE_MAILBOX_PASSWORD"
12 changes: 12 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ jobs:

name: PHP ${{ matrix.php }}

env:
LIVE_MAILBOX: true
LIVE_MAILBOX_DEBUG: true
LIVE_MAILBOX_HOST: mail.example.local
LIVE_MAILBOX_PORT: 993
LIVE_MAILBOX_USERNAME: root@example.local
LIVE_MAILBOX_ENCRYPTION: ssl
LIVE_MAILBOX_PASSWORD: foobar
LIVE_MAILBOX_QUOTA_SUPPORT: true

steps:
- name: Checkout code
uses: actions/checkout@v3
Expand All @@ -34,5 +44,7 @@ jobs:
- name: Install Composer dependencies
run: composer install --prefer-dist --no-interaction --no-progress

- run: "sh .github/docker/dovecot_setup.sh"

- name: Execute tests
run: vendor/bin/phpunit
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Discord: [discord.gg/rd4cN9h6][link-discord]
- [Documentations](#documentations)
- [Compatibility](#compatibility)
- [Basic usage example](#basic-usage-example)
- [Testing](#testing)
- [Known issues](#known-issues)
- [Support](#support)
- [Features & pull requests](#features--pull-requests)
Expand Down Expand Up @@ -97,6 +98,60 @@ foreach($folders as $folder){
```


## Testing
To run the tests, please execute the following command:
```bash
composer test
```

### Quick-Test / Static Test
To disable all test which require a live mailbox, please copy the `phpunit.xml.dist` to `phpunit.xml` and adjust the configuration:
```xml
<php>
<env name="LIVE_MAILBOX" value="false"/>
</php>
```

### Full-Test / Live Mailbox Test
To run all tests, you need to provide a valid imap configuration.

To provide a valid imap configuration, please copy the `phpunit.xml.dist` to `phpunit.xml` and adjust the configuration:
```xml
<php>
<env name="LIVE_MAILBOX" value="true"/>
<env name="LIVE_MAILBOX_DEBUG" value="true"/>
<env name="LIVE_MAILBOX_HOST" value="mail.example.local"/>
<env name="LIVE_MAILBOX_PORT" value="993"/>
<env name="LIVE_MAILBOX_VALIDATE_CERT" value="false"/>
<env name="LIVE_MAILBOX_QUOTA_SUPPORT" value="true"/>
<env name="LIVE_MAILBOX_ENCRYPTION" value="ssl"/>
<env name="LIVE_MAILBOX_USERNAME" value="root@example.local"/>
<env name="LIVE_MAILBOX_PASSWORD" value="foobar"/>
</php>
```

The test account should **not** contain any important data, as it will be deleted during the test.
Furthermore, the test account should be able to create new folders, move messages and should **not** be used by any other
application during the test.

It's recommended to use a dedicated test account for this purpose. You can use the provided `Dockerfile` to create an imap server used for testing purposes.

Build the docker image:
```bash
cd .github/docker

docker build -t php-imap-server .
```
Run the docker image:
```bash
docker run --name imap-server -p 993:993 -it --rm -d php-imap-server
```
Stop the docker image:
```bash
docker stop imap-server
```


### Known issues
| Error | Solution |
|:---------------------------------------------------------------------------|:----------------------------------------------------------------------------------------|
Expand Down
8 changes: 5 additions & 3 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@
</logging>
<php>
<env name="LIVE_MAILBOX" value="false"/>
<env name="LIVE_MAILBOX_HOST" value="localhost"/>
<env name="LIVE_MAILBOX_PORT" value="143"/>
<env name="LIVE_MAILBOX_DEBUG" value="false"/>
<env name="LIVE_MAILBOX_HOST" value="mail.example.local"/>
<env name="LIVE_MAILBOX_PORT" value="993"/>
<env name="LIVE_MAILBOX_VALIDATE_CERT" value="false"/>
<env name="LIVE_MAILBOX_QUOTA_SUPPORT" value="false"/>
<env name="LIVE_MAILBOX_ENCRYPTION" value=""/>
<env name="LIVE_MAILBOX_USERNAME" value="root@example.com"/>
<env name="LIVE_MAILBOX_USERNAME" value="root@example.local"/>
<env name="LIVE_MAILBOX_PASSWORD" value="foobar"/>
</php>
</phpunit>
49 changes: 49 additions & 0 deletions tests/LiveMailboxTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php
/*
* File: LiveMailboxTest.php
* Category: -
* Author: M.Goldenbaum
* Created: 04.03.23 03:52
* Updated: -
*
* Description:
* -
*/

namespace Tests;

use Webklex\PHPIMAP\Exceptions\AuthFailedException;
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
use Webklex\PHPIMAP\Exceptions\ImapBadRequestException;
use Webklex\PHPIMAP\Exceptions\ImapServerErrorException;
use Webklex\PHPIMAP\Exceptions\MaskNotFoundException;
use Webklex\PHPIMAP\Exceptions\ResponseException;
use Webklex\PHPIMAP\Exceptions\RuntimeException;

/**
* Class LiveMailboxTest
*
* @package Tests
*/
class LiveMailboxTest extends LiveMailboxTestCase {

/**
* Test if the connection is working
*
* @return void
* @throws AuthFailedException
* @throws ConnectionFailedException
* @throws ImapBadRequestException
* @throws ImapServerErrorException
* @throws ResponseException
* @throws RuntimeException
* @throws MaskNotFoundException
*/
public function testConnection(): void {
$client = $this->getClient();
$client->connect();

self::assertTrue($client->isConnected());
}

}
120 changes: 120 additions & 0 deletions tests/LiveMailboxTestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php
/*
* File: LiveMailboxTestCase.php
* Category: -
* Author: M.Goldenbaum
* Created: 04.03.23 03:43
* Updated: -
*
* Description:
* -
*/

namespace Tests;

use PHPUnit\Framework\TestCase;
use Webklex\PHPIMAP\Client;
use Webklex\PHPIMAP\ClientManager;
use Webklex\PHPIMAP\Exceptions\AuthFailedException;
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
use Webklex\PHPIMAP\Exceptions\EventNotFoundException;
use Webklex\PHPIMAP\Exceptions\ImapBadRequestException;
use Webklex\PHPIMAP\Exceptions\ImapServerErrorException;
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
use Webklex\PHPIMAP\Exceptions\MaskNotFoundException;
use Webklex\PHPIMAP\Exceptions\MessageContentFetchingException;
use Webklex\PHPIMAP\Exceptions\MessageFlagException;
use Webklex\PHPIMAP\Exceptions\MessageHeaderFetchingException;
use Webklex\PHPIMAP\Exceptions\ResponseException;
use Webklex\PHPIMAP\Exceptions\RuntimeException;
use Webklex\PHPIMAP\Folder;
use Webklex\PHPIMAP\Message;

/**
* Class LiveMailboxTestCase
*
* @package Tests
*/
abstract class LiveMailboxTestCase extends TestCase {
const SPECIAL_CHARS = 'A_\\|!"£$%&()=?àèìòùÀÈÌÒÙ<>-@#[]_ß_б_π_€_✔_你_يد_Z_';

/**
* Client manager
* @var ClientManager $manager
*/
protected static ClientManager $manager;

/**
* Get the client manager
*
* @return ClientManager
*/
final protected function getManager(): ClientManager {
if (!isset(self::$manager)) {
self::$manager = new ClientManager([
'options' => [
"debug" => $_ENV["LIVE_MAILBOX_DEBUG"] ?? false,
],
'accounts' => [
'default' => [
'host' => getenv("LIVE_MAILBOX_HOST"),
'port' => getenv("LIVE_MAILBOX_PORT"),
'encryption' => getenv("LIVE_MAILBOX_ENCRYPTION"),
'validate_cert' => getenv("LIVE_MAILBOX_VALIDATE_CERT"),
'username' => getenv("LIVE_MAILBOX_USERNAME"),
'password' => getenv("LIVE_MAILBOX_PASSWORD"),
'protocol' => 'imap', //might also use imap, [pop3 or nntp (untested)]
],
],
]);
}
return self::$manager;
}

/**
* Get the client
*
* @return Client
* @throws MaskNotFoundException
*/
final protected function getClient(): Client {
return $this->getManager()->account('default');
}

final protected function getSpecialChars(): string {
return self::SPECIAL_CHARS;
}

/**
* Append a message to a folder
* @param Folder $folder
* @param string $message
*
* @return Message
* @throws AuthFailedException
* @throws ConnectionFailedException
* @throws EventNotFoundException
* @throws ImapBadRequestException
* @throws ImapServerErrorException
* @throws InvalidMessageDateException
* @throws MessageContentFetchingException
* @throws MessageFlagException
* @throws MessageHeaderFetchingException
* @throws ResponseException
* @throws RuntimeException
*/
final protected function appendMessage(Folder $folder, string $message): Message {
$response = $folder->appendMessage($message);

if (!isset($response[0])) {
$this->fail("No message ID returned");
}
$test = explode(' ', $response[0]);
if (!isset($test[3])) {
$this->fail("No message ID returned");
}

$id = substr($test[3], 0, -1);
return $folder->messages()->getMessageByUid(intval($id));
}
}
Loading

0 comments on commit f1e3f83

Please sign in to comment.