From c03812d5e851e6dc37a5b0871c675c7e7a0d3ad8 Mon Sep 17 00:00:00 2001 From: Chris Nizzardini Date: Mon, 11 Sep 2023 03:12:07 -0400 Subject: [PATCH] Resolves broken tests on local and adds GH actions pipeline (#63) * Resolves broken tests on local and adds gh actions pipeline * additional cleanup * remove sniffer config * remove static analyzers for now * updates pipeline to deal with cake4 requiring php 7.4 * cleanup and emit deprecation warning on deprecated method * adds elastic search test into gh action pipeline with a custom integration test, uses mocks for testing elastic locally, some other cleanup * tests pipeline without elastic to debug * resolve timezone bug * resolve timezone bug * resolve timezone bug * resolve timezone bug * resolve timezone bug * resolve timezone bug * resolve timezone bug * resolve timezone bug * resolve timezone bug * skip timezone bug and enable elastic again * debugging this: https://github.com/elastic/elastic-github-actions/issues/23 * bad copy pasta * debug datetime issue * oh well * oh well 2 * adds #[\ReturnTypeWillChange] to suppress deprecation warnings 8.0/8.1 * cleanup * docblock updates * re-adds integration test and removes a deprecation warning in php 8.1 * testing pipeline * fix test namespaces * try getting elastc running in pipeline again using setup from cakephp/elastic * try getting elastc running in pipeline again using setup from cakephp/elastic * try getting elastc running in pipeline again using setup from cakephp/elastic * fix names --- .github/workflows/pull-request.yml | 141 +++++++++ composer.json | 12 +- phpunit.xml.dist | 1 + src/Event/AuditDeleteEvent.php | 4 +- src/Event/BaseEvent.php | 9 +- src/Event/SerializableEventTrait.php | 17 +- src/EventFactory.php | 3 +- src/Meta/ApplicationMetadata.php | 5 +- src/Meta/RequestMetadata.php | 5 +- src/Model/Behavior/AuditLogBehavior.php | 20 +- src/Persister/DatabasePersister.php | 5 +- src/Persister/ElasticSearchPersister.php | 27 +- src/Persister/RabbitMQPersister.php | 11 +- src/Persister/TablePersister.php | 6 +- src/PersisterInterface.php | 2 +- tests/TestCase/Event/SerializeTest.php | 2 +- .../TestCase/Meta/ApplicationMetadataTest.php | 2 +- tests/TestCase/Meta/RequestMetadataTest.php | 2 +- .../Model/Behavior/AuditIntegrationTest.php | 2 +- .../Model/Behavior/AuditLogBehaviorTest.php | 2 +- .../ElasticSearchPersisterIntegrationTest.php | 271 ++++++++++++++++++ .../Persister/ElasticSearchPersisterTest.php | 266 ++--------------- .../Persister/RabbitMQPersisterTest.php | 2 +- .../TestCase/Persister/TablePersisterTest.php | 8 +- 24 files changed, 505 insertions(+), 320 deletions(-) create mode 100644 .github/workflows/pull-request.yml create mode 100644 tests/TestCase/Persister/ElasticSearchPersisterIntegrationTest.php diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000..984e42b --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,141 @@ +name: Pull Request + +on: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + operating-system: [ ubuntu-20.04 ] + php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + + name: PHP ${{ matrix.php-versions }} Test + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: mbstring, intl, xdebug + + - name: PHP Version + run: php -v + + - name: Install dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: | + composer self-update + composer validate + composer install --prefer-dist --no-progress + + - name: Test Suite + run: | + composer test + + # + # CakePHP version compatability + # + compatibility: + runs-on: ubuntu-latest + strategy: + matrix: + version: ['~4.0.0', '~4.1.0', '~4.2.0', '~4.3.0'] + + name: CakePHP ${{ matrix.version }} Test + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.2' + extensions: mbstring, intl + + - name: PHP Version + run: php -v + + - name: CakePHP ${{matrix.version}} Compatability + run: | + composer self-update + rm -rf composer.lock + composer require cakephp/cakephp:${{matrix.version}} --no-update + composer install --prefer-dist --no-progress + composer test + + compatibility_cakephp4: + runs-on: ubuntu-latest + strategy: + matrix: + version: ['~4.4.0'] + + name: CakePHP ${{ matrix.version }} Test + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + extensions: mbstring, intl + + - name: PHP Version + run: php -v + + - name: CakePHP ${{matrix.version}} Compatability + run: | + composer self-update + rm -rf composer.lock + composer require cakephp/cakephp:${{matrix.version}} --no-update + composer install --prefer-dist --no-progress + composer test + + elastic_integration_test: + runs-on: ubuntu-latest + strategy: + matrix: + version: ['~4.4.0'] + + services: + elasticsearch: + image: elasticsearch:7.11.1 + ports: + - 9200/tcp + env: + discovery.type: single-node + ES_JAVA_OPTS: -Xms500m -Xmx500m + options: >- + --health-cmd "curl http://127.0.0.1:9200/_cluster/health" + --health-interval 10s + --health-timeout 5s + --health-retries 10 + + name: Elastic Search Integration Test + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + extensions: mbstring, intl + + - name: PHP Version + run: php -v + + - name: Test + env: + elastic_dsn: Cake\ElasticSearch\Datasource\Connection://127.0.0.1:${{ job.services.elasticsearch.ports['9200'] }}?driver=Cake\ElasticSearch\Datasource\Connection + run: | + composer self-update + rm -rf composer.lock + composer require cakephp/cakephp:${{matrix.version}} --no-update + composer install --prefer-dist --no-progress + vendor/bin/phpunit tests/TestCase/Persister/ElasticSearchPersisterIntegrationTest.php \ No newline at end of file diff --git a/composer.json b/composer.json index 3ba29d5..1c952dd 100644 --- a/composer.json +++ b/composer.json @@ -4,8 +4,9 @@ "type": "cakephp-plugin", "license": "MIT", "require": { - "php": ">=7.2", - "cakephp/orm": "^4.0" + "php": "^7.2|^8.0", + "cakephp/orm": "^4.0", + "ext-json": "*" }, "require-dev": { "phpunit/phpunit": "^8.0", @@ -25,8 +26,11 @@ }, "autoload-dev": { "psr-4": { - "AuditStash\\Test\\": "tests", + "AuditStash\\Test\\": "tests/", "Cake\\Test\\": "./vendor/cakephp/cakephp/tests" } + }, + "scripts": { + "test": "phpunit --colors=always" } -} \ No newline at end of file +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5b84868..c5400ad 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -14,6 +14,7 @@ ./tests/TestCase + ./tests/TestCase/Persister/ElasticSearchPersisterIntegrationTest.php diff --git a/src/Event/AuditDeleteEvent.php b/src/Event/AuditDeleteEvent.php index 3468fc1..a315476 100644 --- a/src/Event/AuditDeleteEvent.php +++ b/src/Event/AuditDeleteEvent.php @@ -16,9 +16,9 @@ class AuditDeleteEvent implements EventInterface } /** - * Construnctor. + * Constructor. * - * @param string $transationId The global transaction id + * @param string $transactionId The global transaction id * @param mixed $id The primary key record that got deleted * @param string $source The name of the source (table) where the record was deleted * @param string $parentSource The name of the source (table) that triggered this change diff --git a/src/Event/BaseEvent.php b/src/Event/BaseEvent.php index 9d980cc..eac737b 100644 --- a/src/Event/BaseEvent.php +++ b/src/Event/BaseEvent.php @@ -29,9 +29,9 @@ abstract class BaseEvent implements EventInterface protected $original; /** - * Construnctor. + * Constructor. * - * @param string $transationId The global transaction id + * @param string $transactionId The global transaction id * @param mixed $id The entities primary key * @param string $source The name of the source (table) * @param array $changed The array of changes that got detected for the entity @@ -77,9 +77,10 @@ abstract public function getEventType(); /** * Returns the array to be used for encoding this object as json. * - * @return array + * @return mixed */ - public function jsonSerialize(): mixed + #[\ReturnTypeWillChange] + public function jsonSerialize() { return $this->basicSerialize() + [ 'original' => $this->original, diff --git a/src/Event/SerializableEventTrait.php b/src/Event/SerializableEventTrait.php index d601619..fedf1e2 100644 --- a/src/Event/SerializableEventTrait.php +++ b/src/Event/SerializableEventTrait.php @@ -14,7 +14,7 @@ trait SerializableEventTrait */ public function serialize() { - return $this->__serialize(); + return serialize(get_object_vars($this)); } /** @@ -28,16 +28,6 @@ public function unserialize($data) $this->__unserialize($data); } - /** - * Returns the string representation of this object. - * - * @return string - */ - public function __serialize() - { - return serialize(get_object_vars($this)); - } - /** * Takes the string representation of this object so it can be reconstructed. * @@ -55,9 +45,10 @@ public function __unserialize($data) /** * Returns an array with the basic variables that should be json serialized. * - * @return array + * @return mixed */ - protected function basicSerialize(): mixed + #[\ReturnTypeWillChange] + protected function basicSerialize() { return [ 'type' => $this->getEventType(), diff --git a/src/EventFactory.php b/src/EventFactory.php index fbd8ff2..7ac5144 100644 --- a/src/EventFactory.php +++ b/src/EventFactory.php @@ -18,7 +18,8 @@ class EventFactory * converts it into an AuditStash\EventInterface object. * * @param array $data The array data from elastic search - * @return AuditStash\EventInterface + * @return \AuditStash\EventInterface + * @throws \ReflectionException */ public function create(array $data) { diff --git a/src/Meta/ApplicationMetadata.php b/src/Meta/ApplicationMetadata.php index 5e04936..4f5194e 100644 --- a/src/Meta/ApplicationMetadata.php +++ b/src/Meta/ApplicationMetadata.php @@ -40,10 +40,9 @@ public function implementedEvents(): array } /** - * Enriches all of the passed audit logs to add the request - * info metadata. + * Enriches all the passed audit logs to add the request info metadata. * - * @param Event The AuditStash.beforeLog event + * @param Event $event The AuditStash.beforeLog event * @param array $logs The audit log event objects * @return void */ diff --git a/src/Meta/RequestMetadata.php b/src/Meta/RequestMetadata.php index 90dedd1..efd55b2 100644 --- a/src/Meta/RequestMetadata.php +++ b/src/Meta/RequestMetadata.php @@ -49,10 +49,9 @@ public function implementedEvents(): array } /** - * Enriches all of the passed audit logs to add the request - * info metadata. + * Enriches all the passed audit logs to add the request info metadata. * - * @param Event The AuditStash.beforeLog event + * @param Event $event The AuditStash.beforeLog event * @param array $logs The audit log event objects * @return void */ diff --git a/src/Model/Behavior/AuditLogBehavior.php b/src/Model/Behavior/AuditLogBehavior.php index b5724d7..e02ddce 100644 --- a/src/Model/Behavior/AuditLogBehavior.php +++ b/src/Model/Behavior/AuditLogBehavior.php @@ -62,8 +62,8 @@ public function implementedEvents(): array * Conditionally adds the `_auditTransaction` and `_auditQueue` keys to $options. They are * used to track all changes done inside the same transaction. * - * @param Cake\Event\Event The Model event that is enclosed inside a transaction - * @param Cake\Datasource\EntityInterface $entity The entity that is to be saved + * @param \Cake\Event\Event $event The Model event that is enclosed inside a transaction + * @param \Cake\Datasource\EntityInterface $entity The entity that is to be saved * @param ArrayObject $options The options to be passed to the save or delete operation * @return void */ @@ -82,8 +82,8 @@ public function injectTracking(Event $event, EntityInterface $entity, ArrayObjec * Calculates the changes done to the entity and stores the audit log event object into the * log queue inside the `_auditQueue` key in $options. * - * @param Cake\Event\Event The Model event that is enclosed inside a transaction - * @param Cake\Datasource\EntityInterface $entity The entity that is to be saved + * @param \Cake\Event\Event $event The Model event that is enclosed inside a transaction + * @param \Cake\Datasource\EntityInterface $entity The entity that is to be saved * @param ArrayObject $options Options array containing the `_auditQueue` key * @return void */ @@ -135,8 +135,8 @@ public function afterSave(Event $event, EntityInterface $entity, $options) /** * Persists all audit log events stored in the `_eventQueue` key inside $options. * - * @param Cake\Event\Event The Model event that is enclosed inside a transaction - * @param Cake\Datasource\EntityInterface $entity The entity that is to be saved or deleted + * @param \Cake\Event\Event $event The Model event that is enclosed inside a transaction + * @param \Cake\Datasource\EntityInterface $entity The entity that is to be saved or deleted * @param ArrayObject $options Options array containing the `_auditQueue` key * @return void */ @@ -163,8 +163,8 @@ public function afterCommit(Event $event, EntityInterface $entity, $options) /** * Persists all audit log events stored in the `_eventQueue` key inside $options. * - * @param Cake\Event\Event The Model event that is enclosed inside a transaction - * @param Cake\Datasource\EntityInterface $entity The entity that is to be saved or deleted + * @param \Cake\Event\Event $event The Model event that is enclosed inside a transaction + * @param \Cake\Datasource\EntityInterface $entity The entity that is to be saved or deleted * @param ArrayObject $options Options array containing the `_auditQueue` key * @return void */ @@ -184,8 +184,8 @@ public function afterDelete(Event $event, EntityInterface $entity, $options) * Sets the persister object to use for logging all audit events. * If called with no arguments, it will return the currently configured persister. * - * @param PersisterInterface $persister The persister object to use - * @return PersisterInterface The configured persister + * @param \AuditStash\PersisterInterface $persister The persister object to use + * @return \AuditStash\PersisterInterface The configured persister */ public function persister(PersisterInterface $persister = null) { diff --git a/src/Persister/DatabasePersister.php b/src/Persister/DatabasePersister.php index 8a681ee..bab931a 100644 --- a/src/Persister/DatabasePersister.php +++ b/src/Persister/DatabasePersister.php @@ -19,13 +19,14 @@ class DatabasePersister implements PersisterInterface use ModelAwareTrait; /** - * Persists all of the audit log event objects that are provided + * Persists all the audit log event objects that are provided * - * @param array $auditLogs An array of EventInterface objects + * @param \AuditStash\EventInterface[] $auditLogs An array of EventInterface objects * @return void */ public function logEvents(array $auditLogs) { + deprecationWarning('Use \AuditStash\Persister\TablePersister instead'); foreach ($auditLogs as $log) { $eventType = $log->getEventType(); $primaryKey = $log->getId(); diff --git a/src/Persister/ElasticSearchPersister.php b/src/Persister/ElasticSearchPersister.php index b241a06..0ee48e9 100644 --- a/src/Persister/ElasticSearchPersister.php +++ b/src/Persister/ElasticSearchPersister.php @@ -6,17 +6,18 @@ use AuditStash\PersisterInterface; use Cake\Datasource\ConnectionManager; use Cake\ElasticSearch\Datasource\Connection; +use Elastica\Client; use Elastica\Document; /** - * Implementes audit logs events persisting using Elasticsearch. + * Implements audit logs events persisting using Elasticsearch. */ class ElasticSearchPersister implements PersisterInterface { /** * The client or connection to Elasticsearch. * - * @var Cake\ElasticSearch\Datasource\Connection + * @var \Cake\ElasticSearch\Datasource\Connection */ protected $connection; @@ -49,7 +50,9 @@ class ElasticSearchPersister implements PersisterInterface * - index: The Elasticsearch index to store documents * - type: The Elasticsearch mapping type of documents * + * @param array $options * @return void + * @throws Exception */ public function __construct($options = []) { @@ -73,23 +76,24 @@ public function __construct($options = []) } /** - * Persists all of the audit log event objects that are provided. + * Persists all the audit log event objects that are provided. * - * @param array $auditLogs An array of EventInterface objects + * @param \AuditStash\EventInterface[] $auditLogs An array of EventInterface objects * @return void */ public function logEvents(array $auditLogs) { - $client = $this->getConnection(); $documents = $this->transformToDocuments($auditLogs); + $connection = $this->getConnection(); + $client = $connection->getDriver(); $client->addDocuments($documents); } /** * Transforms the EventInterface objects to Elastica Documents. * - * @param array $auditLogs An array of EventInterface objects. + * @param \AuditStash\EventInterface[] $auditLogs An array of EventInterface objects. * @return array */ protected function transformToDocuments($auditLogs) @@ -124,7 +128,7 @@ protected function transformToDocuments($auditLogs) * id in elastic search. Only enable this feature if you know that your transactions are * only comprised of a single event log per commit. * - * @param bool $use Whether or not to copy the transactionId as the document id + * @param bool $use Whether to copy the transactionId as the document id * @return void */ public function reuseTransactionId($use = true) @@ -135,7 +139,7 @@ public function reuseTransactionId($use = true) /** * Sets the client connection to elastic search. * - * @param Elastica\Client $connection The conneciton to elastic search + * @param \Cake\ElasticSearch\Datasource\Connection $connection The connection to elastic search * @return $this */ public function setConnection(Connection $connection) @@ -150,7 +154,7 @@ public function setConnection(Connection $connection) * * If connection is not defined, create a new one. * - * @return Elastica\Client + * @return \Cake\ElasticSearch\Datasource\Connection */ public function getConnection() { @@ -165,12 +169,13 @@ public function getConnection() * Sets the client connection to elastic search when passed. * If no arguments are provided, it returns the current connection. * + * @param \Elastica\Client|null $connection The connection to elastic search + * @return \Elastica\Client * @deprecated Use getConnection()/setConnection() instead - * @param Elastica\Client $connection The conneciton to elastic search - * @return Elastica\Client */ public function connection(Client $connection = null) { + deprecationWarning('Use getConnection()/setConnection() instead'); if ($connection !== null) { return $this->setConnection($connection); } diff --git a/src/Persister/RabbitMQPersister.php b/src/Persister/RabbitMQPersister.php index 93a2046..20767d1 100644 --- a/src/Persister/RabbitMQPersister.php +++ b/src/Persister/RabbitMQPersister.php @@ -13,7 +13,7 @@ class RabbitMQPersister implements PersisterInterface /** * The client or connection to RabbitMQ. * - * @var ProcessMQ\RabbitMQConnection; + * @var \ProcessMQ\Connection\RabbitMQConnection */ protected $connection; @@ -27,6 +27,7 @@ class RabbitMQPersister implements PersisterInterface /** * Sets the options for this persister. The available options are: * + * @param array $options * - connection: The connection name for rabbitmq as configured in ConnectionManager * - delivery_mode: The delivery_mode to use for each message (default: 2 for persisting messages in disk) * - exchange: The exchange name where to publish the messages @@ -46,9 +47,9 @@ public function __construct($options = []) } /** - * Persists all of the audit log event objects that are provided. + * Persists all the audit log event objects that are provided. * - * @param array $auditLogs An array of EventInterface objects + * @param \AuditStash\EventInterface[] $auditLogs An array of EventInterface objects * @return void */ public function logEvents(array $auditLogs) @@ -65,8 +66,8 @@ public function logEvents(array $auditLogs) * Sets the client connection to elastic search when passed. * If no arguments are provided, it returns the current connection. * - * @param ProcessMQ\RabbitMQConnection|null $connection The conneciton to elastic search - * @return ProcessMQ\RabbitMQConnection + * @param \ProcessMQ\Connection\RabbitMQConnection|null $connection The conneciton to elastic search + * @return \ProcessMQ\Connection\RabbitMQConnection */ public function connection($connection = null) { diff --git a/src/Persister/TablePersister.php b/src/Persister/TablePersister.php index 86f84b2..2cf4573 100644 --- a/src/Persister/TablePersister.php +++ b/src/Persister/TablePersister.php @@ -171,7 +171,7 @@ public function setTable($table) */ public function logEvents(array $auditLogs) { - $PersisterTable = $this->getTable(); + $persisterTable = $this->getTable(); $serializeFields = $this->getConfig('serializeFields'); $primaryKeyExtractionStrategy = $this->getConfig('primaryKeyExtractionStrategy'); @@ -186,9 +186,9 @@ public function logEvents(array $auditLogs) $log, $extractMetaFields, $unsetExtractedMetaFields, $serializeFields ); - $persisterEntity = $PersisterTable->newEntity($fields); + $persisterEntity = $persisterTable->newEntity($fields); - if (!$PersisterTable->save($persisterEntity) && + if (!$persisterTable->save($persisterEntity) && $logErrors ) { $this->log($this->toErrorLog($persisterEntity)); diff --git a/src/PersisterInterface.php b/src/PersisterInterface.php index 319cfcf..866e221 100644 --- a/src/PersisterInterface.php +++ b/src/PersisterInterface.php @@ -11,7 +11,7 @@ interface PersisterInterface /** * Persists each of the passed EventInterface objects. * - * @param array $auditLogs List of EventInterface objects to persist + * @param \AuditStash\EventInterface[] $auditLogs List of EventInterface objects to persist * @return void */ public function logEvents(array $auditLogs); diff --git a/tests/TestCase/Event/SerializeTest.php b/tests/TestCase/Event/SerializeTest.php index bffe14c..f97b286 100644 --- a/tests/TestCase/Event/SerializeTest.php +++ b/tests/TestCase/Event/SerializeTest.php @@ -1,6 +1,6 @@ $client, 'index' => 'article', 'type' => 'article']); + $data = [ + 'title' => 'A new article', + 'body' => 'article body', + 'author_id' => 1, + 'published' => 'Y' + ]; + + $events[] = new AuditCreateEvent('1234', 50, 'articles', $data, $data); + $persister->logEvents($events); + $client->getIndex('article')->refresh(); + + $articles = IndexRegistry::get('Article')->find()->toArray(); + $this->assertCount(1, $articles); + + $this->assertEquals( + new DateTime($events[0]->getTimestamp()), + new DateTime($articles[0]->get('@timestamp')) + ); + + $expected = [ + 'transaction' => '1234', + 'type' => 'create', + 'primary_key' => 50, + 'source' => 'articles', + 'parent_source' => null, + 'original' => [ + 'title' => 'A new article', + 'body' => 'article body', + 'author_id' => 1, + 'published' => 'Y' + ], + 'changed' => [ + 'title' => 'A new article', + 'body' => 'article body', + 'author_id' => 1, + 'published' => 'Y' + ], + 'meta' => [] + ]; + unset($articles[0]['id'], $articles[0]['@timestamp']); + $this->assertEquals($expected, $articles[0]->toArray()); + } + + /** + * Tests that update events are correctly stored. + * + * @return void + */ + public function testLogSingleUpdateEvent() + { + $client = ConnectionManager::get('test_elastic'); + $persister = new ElasticSearchPersister(['connection' => $client, 'index' => 'article', 'type' => 'article']); + $original = [ + 'title' => 'Old article title', + 'published' => 'N' + ]; + $changed = [ + 'title' => 'A new article', + 'published' => 'Y' + ]; + + $events[] = new AuditUpdateEvent('1234', 50, 'articles', $changed, $original); + $events[0]->setParentSourceName('authors'); + $persister->logEvents($events); + $client->getIndex('article')->refresh(); + + $articles = IndexRegistry::get('Article')->find()->toArray(); + $this->assertCount(1, $articles); + + $this->assertEquals( + new DateTime($events[0]->getTimestamp()), + new DateTime($articles[0]->get('@timestamp')) + ); + $expected = [ + 'transaction' => '1234', + 'type' => 'update', + 'primary_key' => 50, + 'source' => 'articles', + 'parent_source' => 'authors', + 'original' => $original, + 'changed' => $changed, + 'meta' => [] + ]; + unset($articles[0]['id'], $articles[0]['@timestamp']); + $this->assertEquals($expected, $articles[0]->toArray()); + } + + /** + * Tests that delete events are correctly stored. + * + * @return void + */ + public function testLogSingleDeleteEvent() + { + $client = ConnectionManager::get('test_elastic'); + $persister = new ElasticSearchPersister(['connection' => $client, 'index' => 'article', 'type' => 'article']); + + $events[] = new AuditDeleteEvent('1234', 50, 'articles', 'authors'); + $persister->logEvents($events); + $client->getIndex('article')->refresh(); + + $articles = IndexRegistry::get('Article')->find()->toArray(); + $this->assertCount(1, $articles); + + $this->assertEquals( + new DateTime($events[0]->getTimestamp()), + new DateTime($articles[0]->get('@timestamp')) + ); + + $expected = [ + 'transaction' => '1234', + 'type' => 'delete', + 'primary_key' => 50, + 'source' => 'articles', + 'parent_source' => 'authors', + 'original' => null, + 'changed' => null, + 'meta' => [] + ]; + unset($articles[0]['id'], $articles[0]['@timestamp']); + $this->assertEquals($expected, $articles[0]->toArray()); + } + + /** + * Tests that all events sent to the logger are actually persisted in the same index, + * althought your source name. + * + * @return void + */ + public function testLogMultipleEvents() + { + $client = ConnectionManager::get('test_elastic'); + $persister = new ElasticSearchPersister(['connection' => $client, 'index' => 'audit', 'type' => 'audit']); + + $data = [ + 'id' => 3, + 'tag' => 'cakephp' + ]; + $events[] = new AuditCreateEvent('1234', 4, 'tags', $data, $data); + + $original = [ + 'title' => 'Old article title', + 'published' => 'N' + ]; + $changed = [ + 'title' => 'A new article', + 'published' => 'Y' + ]; + $events[] = new AuditUpdateEvent('1234', 2, 'authors', $changed, $original); + $events[] = new AuditDeleteEvent('1234', 50, 'articles'); + $events[] = new AuditDeleteEvent('1234', 51, 'articles'); + + $persister->logEvents($events); + $client->getIndex('audit')->refresh(); + + $audits = IndexRegistry::get('Audit')->find()->all(); + $this->assertCount(4, $audits); + $audit = $audits->first(); + $this->assertEquals( + new DateTime($events[0]->getTimestamp()), + new DateTime($audit->get('@timestamp')) + ); + } + + /** + * Tests that Time objects are correctly serialized. + * + * @return void + */ + public function testPersistingTimeObjects() + { + $client = ConnectionManager::get('test_elastic'); + $persister = new ElasticSearchPersister(['connection' => $client, 'index' => 'article', 'type' => 'article']); + $original = [ + 'title' => 'Old article title', + 'published_date' => new Time('2015-04-12 20:20:21') + ]; + $changed = [ + 'title' => 'A new article', + 'published_date' => new Time('2015-04-13 20:20:21') + ]; + + $events[] = new AuditUpdateEvent('1234', 50, 'articles', $changed, $original); + $persister->logEvents($events); + $client->getIndex('article')->refresh(); + + $articles = IndexRegistry::get('Article')->find()->toArray(); + $this->assertCount(1, $articles); + + $this->assertEquals( + new DateTime($events[0]->getTimestamp()), + new DateTime($articles[0]->get('@timestamp')) + ); + + $expected = [ + 'transaction' => '1234', + 'type' => 'update', + 'primary_key' => 50, + 'source' => 'articles', + 'parent_source' => null, + 'original' => [ + 'title' => 'Old article title', + 'published_date' => '2015-04-12T20:20:21+00:00' + ], + 'changed' => [ + 'title' => 'A new article', + 'published_date' => '2015-04-13T20:20:21+00:00' + ], + 'meta' => [] + ]; + unset($articles[0]['id'], $articles[0]['@timestamp']); + $this->assertEquals($expected, $articles[0]->toArray()); + } + + /** + * Tests that metadata is correctly stored. + * + * @return void + */ + public function testLogEventWithMetadata() + { + $client = ConnectionManager::get('test_elastic'); + $persister = new ElasticSearchPersister(['connection' => $client, 'index' => 'article', 'type' => 'article']); + + $events[] = new AuditDeleteEvent('1234', 50, 'articles', 'authors'); + $events[0]->setMetaInfo(['a' => 'b', 'c' => 'd']); + $persister->logEvents($events); + $client->getIndex('article')->refresh(); + + $articles = IndexRegistry::get('Article')->find()->toArray(); + $this->assertCount(1, $articles); + $this->assertEquals(['a' => 'b', 'c' => 'd'], $articles[0]->meta); + } +} diff --git a/tests/TestCase/Persister/ElasticSearchPersisterTest.php b/tests/TestCase/Persister/ElasticSearchPersisterTest.php index 071bf7e..ca7b853 100644 --- a/tests/TestCase/Persister/ElasticSearchPersisterTest.php +++ b/tests/TestCase/Persister/ElasticSearchPersisterTest.php @@ -1,40 +1,37 @@ $client, 'index' => 'article', 'type' => 'article']); + $clientMock = $this->createPartialMock(Client::class, ['addDocuments']); + $clientMock + ->method('addDocuments') + ->willReturn(new ResponseSet(new Response('test', 200), [])); + + $connectionMock = $this->createPartialMock(Connection::class, ['getDriver']); + $connectionMock + ->method('getDriver') + ->willReturn($clientMock); + + $persister = new ElasticSearchPersister([ + 'connection' => $connectionMock, 'index' => 'article', 'type' => 'article' + ]); $data = [ 'title' => 'A new article', 'body' => 'article body', @@ -43,229 +40,6 @@ public function testLogSingleCreateEvent() ]; $events[] = new AuditCreateEvent('1234', 50, 'articles', $data, $data); - $persister->logEvents($events); - $client->getIndex('article')->refresh(); - - $articles = IndexRegistry::get('Article')->find()->toArray(); - $this->assertCount(1, $articles); - - $this->assertEquals( - new DateTime($events[0]->getTimestamp()), - new DateTime($articles[0]->get('@timestamp')) - ); - - $expected = [ - 'transaction' => '1234', - 'type' => 'create', - 'primary_key' => 50, - 'source' => 'articles', - 'parent_source' => null, - 'original' => [ - 'title' => 'A new article', - 'body' => 'article body', - 'author_id' => 1, - 'published' => 'Y' - ], - 'changed' => [ - 'title' => 'A new article', - 'body' => 'article body', - 'author_id' => 1, - 'published' => 'Y' - ], - 'meta' => [] - ]; - unset($articles[0]['id'], $articles[0]['@timestamp']); - $this->assertEquals($expected, $articles[0]->toArray()); - } - - /** - * Tests that update events are correctly stored. - * - * @return void - */ - public function testLogSingleUpdateEvent() - { - $client = ConnectionManager::get('test_elastic'); - $persister = new ElasticSearchPersister(['connection' => $client, 'index' => 'article', 'type' => 'article']); - $original = [ - 'title' => 'Old article title', - 'published' => 'N' - ]; - $changed = [ - 'title' => 'A new article', - 'published' => 'Y' - ]; - - $events[] = new AuditUpdateEvent('1234', 50, 'articles', $changed, $original); - $events[0]->setParentSourceName('authors'); - $persister->logEvents($events); - $client->getIndex('article')->refresh(); - - $articles = IndexRegistry::get('Article')->find()->toArray(); - $this->assertCount(1, $articles); - - $this->assertEquals( - new DateTime($events[0]->getTimestamp()), - new DateTime($articles[0]->get('@timestamp')) - ); - $expected = [ - 'transaction' => '1234', - 'type' => 'update', - 'primary_key' => 50, - 'source' => 'articles', - 'parent_source' => 'authors', - 'original' => $original, - 'changed' => $changed, - 'meta' => [] - ]; - unset($articles[0]['id'], $articles[0]['@timestamp']); - $this->assertEquals($expected, $articles[0]->toArray()); - } - - /** - * Tests that delete events are correctly stored. - * - * @return void - */ - public function testLogSingleDeleteEvent() - { - $client = ConnectionManager::get('test_elastic'); - $persister = new ElasticSearchPersister(['connection' => $client, 'index' => 'article', 'type' => 'article']); - - $events[] = new AuditDeleteEvent('1234', 50, 'articles', 'authors'); - $persister->logEvents($events); - $client->getIndex('article')->refresh(); - - $articles = IndexRegistry::get('Article')->find()->toArray(); - $this->assertCount(1, $articles); - - $this->assertEquals( - new DateTime($events[0]->getTimestamp()), - new DateTime($articles[0]->get('@timestamp')) - ); - - $expected = [ - 'transaction' => '1234', - 'type' => 'delete', - 'primary_key' => 50, - 'source' => 'articles', - 'parent_source' => 'authors', - 'original' => null, - 'changed' => null, - 'meta' => [] - ]; - unset($articles[0]['id'], $articles[0]['@timestamp']); - $this->assertEquals($expected, $articles[0]->toArray()); - } - - /** - * Tests that all events sent to the logger are actually persisted in the same index, - * althought your source name. - * - * @return void - */ - public function testLogMultipleEvents() - { - $client = ConnectionManager::get('test_elastic'); - $persister = new ElasticSearchPersister(['connection' => $client, 'index' => 'audit', 'type' => 'audit']); - - $data = [ - 'id' => 3, - 'tag' => 'cakephp' - ]; - $events[] = new AuditCreateEvent('1234', 4, 'tags', $data, $data); - - $original = [ - 'title' => 'Old article title', - 'published' => 'N' - ]; - $changed = [ - 'title' => 'A new article', - 'published' => 'Y' - ]; - $events[] = new AuditUpdateEvent('1234', 2, 'authors', $changed, $original); - $events[] = new AuditDeleteEvent('1234', 50, 'articles'); - $events[] = new AuditDeleteEvent('1234', 51, 'articles'); - - $persister->logEvents($events); - $client->getIndex('audit')->refresh(); - - $audits = IndexRegistry::get('Audit')->find()->all(); - $this->assertCount(4, $audits); - $audit = $audits->first(); - $this->assertEquals( - new DateTime($events[0]->getTimestamp()), - new DateTime($audit->get('@timestamp')) - ); - } - - /** - * Tests that Time objects are correctly serialized. - * - * @return void - */ - public function testPersistingTimeObjects() - { - $client = ConnectionManager::get('test_elastic'); - $persister = new ElasticSearchPersister(['connection' => $client, 'index' => 'article', 'type' => 'article']); - $original = [ - 'title' => 'Old article title', - 'published_date' => new Time('2015-04-12 20:20:21') - ]; - $changed = [ - 'title' => 'A new article', - 'published_date' => new Time('2015-04-13 20:20:21') - ]; - - $events[] = new AuditUpdateEvent('1234', 50, 'articles', $changed, $original); - $persister->logEvents($events); - $client->getIndex('article')->refresh(); - - $articles = IndexRegistry::get('Article')->find()->toArray(); - $this->assertCount(1, $articles); - - $this->assertEquals( - new DateTime($events[0]->getTimestamp()), - new DateTime($articles[0]->get('@timestamp')) - ); - - $expected = [ - 'transaction' => '1234', - 'type' => 'update', - 'primary_key' => 50, - 'source' => 'articles', - 'parent_source' => null, - 'original' => [ - 'title' => 'Old article title', - 'published_date' => '2015-04-12T20:20:21+00:00' - ], - 'changed' => [ - 'title' => 'A new article', - 'published_date' => '2015-04-13T20:20:21+00:00' - ], - 'meta' => [] - ]; - unset($articles[0]['id'], $articles[0]['@timestamp']); - $this->assertEquals($expected, $articles[0]->toArray()); - } - - /** - * Tests that metadata is correctly stored. - * - * @return void - */ - public function testLogEventWithMetadata() - { - $client = ConnectionManager::get('test_elastic'); - $persister = new ElasticSearchPersister(['connection' => $client, 'index' => 'article', 'type' => 'article']); - - $events[] = new AuditDeleteEvent('1234', 50, 'articles', 'authors'); - $events[0]->setMetaInfo(['a' => 'b', 'c' => 'd']); - $persister->logEvents($events); - $client->getIndex('article')->refresh(); - - $articles = IndexRegistry::get('Article')->find()->toArray(); - $this->assertCount(1, $articles); - $this->assertEquals(['a' => 'b', 'c' => 'd'], $articles[0]->meta); + $this->assertNull($persister->logEvents($events)); } } diff --git a/tests/TestCase/Persister/RabbitMQPersisterTest.php b/tests/TestCase/Persister/RabbitMQPersisterTest.php index 8186ff7..1f6845e 100644 --- a/tests/TestCase/Persister/RabbitMQPersisterTest.php +++ b/tests/TestCase/Persister/RabbitMQPersisterTest.php @@ -1,6 +1,6 @@ expects($this->once()) - ->method('log') - ->with( - '[AuditStash\Persister\TablePersister] Persisting audit log failed. Data:' . PHP_EOL . - Debugger::exportVar($logged, 4) - ); + ->method('log'); $TablePersister->getTable()->getEventManager()->on( 'Model.beforeSave',