diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a67afc..7d78f24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,6 +80,9 @@ jobs: - name: Run UnitTests if: matrix.ssl_test == 'no' run: ./vendor/bin/phpunit + env: + SSL_TEST: "no" + TEST_RABBITMQ_CONNECTION_URI: "amqp://guest:guest@localhost:5672/" - name: Run UnitTests if: matrix.ssl_test == 'yes' run: ./vendor/bin/phpunit @@ -87,15 +90,17 @@ jobs: SSL_TEST: "yes" SSL_CA: "ssl/ca.pem" SSL_PEER_NAME: "server.rmq" + TEST_RABBITMQ_CONNECTION_URI: "amqp://guest:guest@localhost:5672/" - name: Run UnitTests if: matrix.ssl_test == 'client' run: ./vendor/bin/phpunit env: - SSL_TEST: "yes" + SSL_TEST: "client" SSL_CA: "ssl/ca.pem" SSL_PEER_NAME: "server.rmq" SSL_CLIENT_CERT: "ssl/client.pem" SSL_CLIENT_KEY: "ssl/client.key" + TEST_RABBITMQ_CONNECTION_URI: "amqp://guest:guest@localhost:5672/" - name: Docker Logs if: ${{ failure() }} run: docker logs rabbitmq diff --git a/README.md b/README.md index 1524f45..230c387 100644 --- a/README.md +++ b/README.md @@ -211,18 +211,14 @@ There is [amqp interop](https://github.com/queue-interop/amqp-interop) compatibl ## Testing -You need access to a RabbitMQ instance to run the test suite. You can either connect to an existing instance or use the -provided Docker Compose setup to create an isolated environment, including a RabbitMQ container, to run the test suite -in. +Create client/server SSL certificates by running: -**Local RabbitMQ** +``` +$ cd test/ssl && make all && cd - +``` -- Change `TEST_RABBITMQ_CONNECTION_URI` in `phpunit.xml` to fit your environment. Then run: +You need access to a RabbitMQ instance in order to run the test suite. The easiest way is to use the provided Docker Compose setup to create an isolated environment, including a RabbitMQ container, to run the test suite in. - ``` - $ vendor/bin/phpunit - ``` - **Docker Compose** - Use Docker Compose to create a network with a RabbitMQ container and a PHP container to run the tests in. The project diff --git a/docker-compose.yml b/docker-compose.yml index c9d3367..52a6284 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,18 +18,19 @@ services: - main hostname: rabbit_node_1 ports: - - "15672:15672" - "5672:5672" + - "5673:5673" + - "15672:15672" tty: true bunny: build: docker/bunny - init: true environment: - SSL_TEST: 'yes' - SSL_CA: ssl/ca.pem - SSL_PEER_NAME: server.rmq - SSL_CLIENT_CERT: ssl/client.pem - SSL_CLIENT_KEY: ssl/client.key + SSL_TEST: "client" + SSL_CA: "ssl/ca.pem" + SSL_CLIENT_CERT: "ssl/client.pem" + SSL_CLIENT_KEY: "ssl/client.key" + SSL_PEER_NAME: "server.rmq" + TEST_RABBITMQ_CONNECTION_URI: "amqp://testuser:testpassword@bunny_rabbit_node_1_1:5672/testvhost" volumes: - .:/opt/bunny networks: diff --git a/docker/bunny/Dockerfile b/docker/bunny/Dockerfile index ec8306f..4304c13 100644 --- a/docker/bunny/Dockerfile +++ b/docker/bunny/Dockerfile @@ -1,4 +1,4 @@ -FROM php:7.4-cli +FROM php:8.1-cli RUN apt-get update \ && apt-get dist-upgrade -y \ @@ -10,6 +10,7 @@ RUN apt-get update \ RUN pecl install xdebug \ && docker-php-ext-enable xdebug +RUN echo "xdebug.mode=coverage" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini RUN curl --silent --show-error https://getcomposer.org/installer | php RUN mv composer.phar /usr/local/bin/composer diff --git a/docker/rabbitmq/entrypoint.sh b/docker/rabbitmq/entrypoint.sh index 47334f6..32aa26a 100755 --- a/docker/rabbitmq/entrypoint.sh +++ b/docker/rabbitmq/entrypoint.sh @@ -1,26 +1,37 @@ #!/bin/bash -set -eu +set -eux is_rabbitmq_gte_3_7_0() { [[ "3.7.0" = $(echo -e "3.7.0\n$RABBITMQ_VERSION" | sort -V | head -n1) ]] } +install_certs() { + local certs_dir="/rabbitmq-certs" + + rm -rf "${certs_dir}" + mkdir "${certs_dir}" + cp "${TEST_DATA_ROOT}"/{ca.pem,server.pem,server.key} "${certs_dir}" + chown rabbitmq "${certs_dir}"/{ca.pem,server.pem,server.key} + chmod 0400 "${certs_dir}"/{ca.pem,server.pem,server.key} +} + +install_config() { + if [[ -n "$CONFIG_NAME" ]]; then + if is_rabbitmq_gte_3_7_0; then + cp "${TEST_DATA_ROOT}/${CONFIG_NAME}.conf" /etc/rabbitmq/rabbitmq.conf + chown rabbitmq /etc/rabbitmq/rabbitmq.conf + else + cp "${TEST_DATA_ROOT}/${CONFIG_NAME}.config" /etc/rabbitmq/rabbitmq.config + chown rabbitmq /etc/rabbitmq/rabbitmq.config + fi + fi +} + TEST_DATA_ROOT=/opt/bunny/test/ssl CONFIG_NAME=${CONFIG_NAME:-} -cp ${TEST_DATA_ROOT}/{ca.pem,server.pem,server.key} /etc/rabbitmq/ -chown rabbitmq /etc/rabbitmq/{ca.pem,server.pem,server.key} -chmod 0400 /etc/rabbitmq/{ca.pem,server.pem,server.key} - -if [[ -n "$CONFIG_NAME" ]]; then - if is_rabbitmq_gte_3_7_0; then - cp ${TEST_DATA_ROOT}/${CONFIG_NAME}.conf /etc/rabbitmq/rabbitmq.conf - chown rabbitmq /etc/rabbitmq/rabbitmq.conf - else - cp ${TEST_DATA_ROOT}/${CONFIG_NAME}.config /etc/rabbitmq/rabbitmq.config - chown rabbitmq /etc/rabbitmq/rabbitmq.config - fi -fi +install_certs +install_config exec "$@" diff --git a/phpunit.xml b/phpunit.xml index ae0be8c..d353aad 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,31 +1,13 @@ - - - - - test/Bunny - - - - - - src - - - - - - - - - - - + + + + src + + + + + test/Bunny + + diff --git a/test/Bunny/SSLTest.php b/test/Bunny/SSLTest.php index edba721..19ecc7a 100644 --- a/test/Bunny/SSLTest.php +++ b/test/Bunny/SSLTest.php @@ -6,6 +6,7 @@ use Bunny\Exception\ClientException; use Bunny\Test\Exception\TimeoutException; use Bunny\Test\Library\AsynchronousClientHelper; +use Bunny\Test\Library\Environment; use Bunny\Test\Library\SynchronousClientHelper; use PHPUnit\Framework\TestCase; @@ -13,7 +14,6 @@ use function dirname; use function file_exists; -use function getenv; use function is_file; use function putenv; @@ -78,7 +78,9 @@ public function testConnectWithMissingClientCert() // let's try without client certificate - it should fail unset($options['ssl']['local_cert'], $options['ssl']['local_pk']); - $this->expectException(ClientException::class); + if (Environment::getSslTest() === 'client') { + $this->expectException(ClientException::class); + } $client = $this->helper->createClient($options); $client->connect(); @@ -112,26 +114,20 @@ public function testConnectWithWrongPeerName() protected function getOptions() { // should we do SSL-tests - if (empty(getenv('SSL_TEST'))) { - $this->markTestSkipped('Skipped due empty ENV-variable "SSL_TEST"'); + if (!in_array(Environment::getSslTest(), ['yes', 'client'], true)) { + $this->markTestSkipped('Skipped because env var SSL_TEST not set to "yes" or "client"'); } // checking CA-file - $caFile = getenv('SSL_CA'); - if (empty($caFile)) { - $this->fail('Missing CA file ENV-variable: "SSL_CA"'); - } + $caFile = Environment::getSslCa(); + $testsDir = dirname(__DIR__); $caFile = $testsDir . '/' . $caFile; if (!file_exists($caFile) || !is_file($caFile)) { $this->fail('Missing CA file: "' . $caFile . '"'); } - $peerName = getenv('SSL_PEER_NAME'); - if (empty($peerName)) { - // setting default value from tests/ssl/Makefile - $peerName = 'server.rmq'; - } + $peerName = Environment::getSslPeerName(); // minimal SSL-options $options = [ @@ -145,8 +141,9 @@ protected function getOptions() ]; - $certFile = getenv('SSL_CLIENT_CERT'); - $keyFile = getenv('SSL_CLIENT_KEY'); + $certFile = Environment::getSslClientCert(); + $keyFile = Environment::getSslClientKey(); + if (!empty($certFile) && !empty($keyFile)) { $certFile = $testsDir . '/' . $certFile; $keyFile = $testsDir . '/' . $keyFile; @@ -159,6 +156,7 @@ protected function getOptions() $options['ssl']['local_cert'] = $certFile; $options['ssl']['local_pk'] = $keyFile; } + return $options; } } diff --git a/test/Library/AsynchronousClientHelper.php b/test/Library/AsynchronousClientHelper.php index a112442..691854c 100644 --- a/test/Library/AsynchronousClientHelper.php +++ b/test/Library/AsynchronousClientHelper.php @@ -29,7 +29,7 @@ public function getDefaultOptions(): array { $options = []; - $options = array_merge($options, $this->parseAmqpUri(getenv('TEST_RABBITMQ_CONNECTION_URI'))); + $options = array_merge($options, $this->parseAmqpUri(Environment::getTestRabbitMqConnectionUri())); return $options; } diff --git a/test/Library/Environment.php b/test/Library/Environment.php new file mode 100644 index 0000000..817f34f --- /dev/null +++ b/test/Library/Environment.php @@ -0,0 +1,65 @@ + run all SSL tests, expect peer cert (c.f. rabbitmq.ssl.verify_peer.conf) + * - "yes" -> run all SSL tests, do *not* expect peer cert (c.f. rabbitmq.ssl.verify_none.conf) + * - "no" (default) -> skip SSL-related tests + */ + public static function getSslTest(): string + { + $value = self::getenv('SSL_TEST', 'no'); + + switch ($value) { + case 'client': + case 'no': + case 'yes': + return $value; + } + + throw new EnvironmentException('SSL_TEST'); + } + + public static function getTestRabbitMqConnectionUri(): string + { + return trim(self::getenv('TEST_RABBITMQ_CONNECTION_URI')); + } + + private static function getenv(string $envVariable, ?string $default = null):string + { + $value = getenv($envVariable); + + if ($value === false && $default === null) { + throw new EnvironmentException($envVariable); + } + + return $value!==false?$value:$default; + } +} diff --git a/test/Library/EnvironmentException.php b/test/Library/EnvironmentException.php new file mode 100644 index 0000000..7693a75 --- /dev/null +++ b/test/Library/EnvironmentException.php @@ -0,0 +1,15 @@ +parseAmqpUri(getenv('TEST_RABBITMQ_CONNECTION_URI'))); + $options = array_merge($options, $this->parseAmqpUri(Environment::getTestRabbitMqConnectionUri())); return $options; }