diff --git a/.travis.yml b/.travis.yml index 3530a8c..5f7df5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,14 @@ addons: language: php matrix: include: + - php: 7.2 + env: + - MAGENTO_VERSION=2.2 + - TEST_SUITE=unit + - php: 7.3 + env: + - MAGENTO_VERSION=2.3-develop + - TEST_SUITE=unit - php: 7.2 env: - MAGENTO_VERSION=2.2 diff --git a/.travis/before_script.sh b/.travis/before_script.sh index 154ddc6..d8d55d4 100755 --- a/.travis/before_script.sh +++ b/.travis/before_script.sh @@ -25,8 +25,13 @@ cd magento2 composer config minimum-stability dev composer config repositories.travis_to_test git https://github.com/$TRAVIS_REPO_SLUG.git #TODO make it work with tags as well: + composer require ${COMPOSER_PACKAGE_NAME}:dev-${TRAVIS_BRANCH}\#{$TRAVIS_COMMIT} +# Install dev dependencies of module +php ../.travis/merge-dev.php vendor/$COMPOSER_PACKAGE_NAME/composer.json composer.json +composer update + # prepare for test suite case $TEST_SUITE in integration) diff --git a/.travis/merge-dev.php b/.travis/merge-dev.php new file mode 100644 index 0000000..8f411c3 --- /dev/null +++ b/.travis/merge-dev.php @@ -0,0 +1,23 @@ + $value) { + $pathPrefix = dirname($fromFile) . DIRECTORY_SEPARATOR; + $fromJson['autoload-dev']['psr-4'][$key] = $pathPrefix . $value; +} + +$toJson['require-dev'] = array_replace_recursive($toJson['require-dev'] ?? [], $fromJson['require-dev']); +$toJson['autoload-dev'] = array_merge_recursive($toJson['autoload-dev'] ?? [], $fromJson['autoload-dev']); + +file_put_contents($toFile, json_encode($toJson, JSON_PRETTY_PRINT)); \ No newline at end of file diff --git a/composer.json b/composer.json index 656f1ed..390bd6c 100755 --- a/composer.json +++ b/composer.json @@ -26,10 +26,15 @@ "src/registration.php" ], "psr-4": { - "IntegerNet\\AsyncVarnish\\Test\\": "tests/", "IntegerNet\\AsyncVarnish\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "IntegerNet\\AsyncVarnish\\Test\\": "tests/", + "IntegerNet\\AsyncVarnish\\": "tests/src/" + } + }, "repositories": [ { "type": "composer", @@ -37,6 +42,6 @@ } ], "require-dev": { - "magento/magento-coding-standard": "@dev" + "react/http": "0.8.5" } } diff --git a/src/Api/TagRepositoryInterface.php b/src/Api/TagRepositoryInterface.php new file mode 100644 index 0000000..0e9048c --- /dev/null +++ b/src/Api/TagRepositoryInterface.php @@ -0,0 +1,37 @@ +tagRepository->getAll(); $maxHeaderLength = $this->getMaxHeaderLengthFromConfig(); diff --git a/src/Model/ResourceModel/Tag.php b/src/Model/ResourceModel/Tag.php index f3120eb..1daab7b 100644 --- a/src/Model/ResourceModel/Tag.php +++ b/src/Model/ResourceModel/Tag.php @@ -22,11 +22,10 @@ public function getMaxTagId(int $limit):array $connection = $this->getConnection(); $subSetSelect = $connection->select()->from( - self::TABLE_NAME, + $this->getTable(self::TABLE_NAME), ['entity_id','tag'] )->order( - 'entity_id', - 'ASC' + 'entity_id ASC' )->limit( $limit ); @@ -44,7 +43,7 @@ public function getUniqueTagsByMaxId(int $maxId):array $connection = $this->getConnection(); $select = $connection->select()->from( - ['main_table' => self::TABLE_NAME], + ['main_table' => $this->getTable(self::TABLE_NAME)], ['tag'] )->group( 'tag' diff --git a/src/Model/TagRepository.php b/src/Model/TagRepository.php index b556291..c1229d6 100644 --- a/src/Model/TagRepository.php +++ b/src/Model/TagRepository.php @@ -3,11 +3,12 @@ namespace IntegerNet\AsyncVarnish\Model; +use IntegerNet\AsyncVarnish\Api\TagRepositoryInterface; use Magento\Framework\App\ResourceConnection; use IntegerNet\AsyncVarnish\Model\ResourceModel\Tag as TagResource; use Magento\Framework\App\Config\ScopeConfigInterface; -class TagRepository +class TagRepository implements TagRepositoryInterface { /** * DB Storage table name @@ -19,6 +20,9 @@ class TagRepository */ const FETCH_TAG_LIMIT_CONFIG_PATH = 'system/full_page_cache/async_varnish/varnish_fetch_tag_limit'; + /** + * @var int|null + */ private $lastUsedId; /** @@ -52,9 +56,9 @@ public function __construct( $this->scopeConfig = $scopeConfig; } - private function getTagFetchLimit() + private function getTagFetchLimit(): int { - return $this->scopeConfig->getValue(self::FETCH_TAG_LIMIT_CONFIG_PATH); + return (int) $this->scopeConfig->getValue(self::FETCH_TAG_LIMIT_CONFIG_PATH); } /** @@ -64,7 +68,7 @@ private function getTagFetchLimit() * @return int * @throws \Exception */ - public function insertMultiple($tags = []) + public function insertMultiple($tags = []): int { if (empty($tags)) { return 0; @@ -92,7 +96,7 @@ function ($tag) { * @return int * @throws \Exception */ - public function deleteUpToId($maxId = 0) + public function deleteUpToId(int $maxId = 0): int { try { $tableName = $this->resource->getTableName(self::TABLE_NAME); @@ -103,10 +107,9 @@ public function deleteUpToId($maxId = 0) } /** - * @return array * @throws \Zend_Db_Statement_Exception */ - public function getAll() + public function getAll(): array { $tags = []; @@ -120,24 +123,21 @@ public function getAll() return $tags; } - $maxId = $maxIdResult['max_id']; + $maxId = (int)$maxIdResult['max_id']; - $uniqueTagsResult = $tagResource->getUniqueTagsByMaxId((int)$maxId); + $uniqueTagsResult = $tagResource->getUniqueTagsByMaxId($maxId); if (!empty($uniqueTagsResult)) { $this->lastUsedId = $maxId; foreach ($uniqueTagsResult as $tag) { - $tags[] = ($tag['tag']); + $tags[] = $tag['tag']; } } return $tags; } - /** - * @return int - */ - public function getLastUsedId() + public function getLastUsedId(): int { return $this->lastUsedId ?: 0; } diff --git a/src/etc/di.xml b/src/etc/di.xml index 6e6a05b..5673de7 100644 --- a/src/etc/di.xml +++ b/src/etc/di.xml @@ -12,4 +12,6 @@ + \ No newline at end of file diff --git a/tests/Integration/AbstractTagRepositoryTest.php b/tests/Integration/AbstractTagRepositoryTest.php new file mode 100644 index 0000000..1a13dc9 --- /dev/null +++ b/tests/Integration/AbstractTagRepositoryTest.php @@ -0,0 +1,78 @@ +tagRepository = $this->getTestSubject(); + } + + abstract protected function getTestSubject(): TagRepositoryInterface; + + public function testInsertAndRetrieve() + { + $affected = $this->tagRepository->insertMultiple(['x', 'y', 'z']); + $this->assertEquals(3, $affected, 'insertMultiple() should return number of inserted rows'); + $this->assertEqualsCanonicalizing(['x', 'y', 'z'], $this->tagRepository->getAll()); + } + + public function testNoDuplicatesAreRetrieved() + { + $affected = $this->tagRepository->insertMultiple(['x', 'y', 'x']); + $this->assertEquals(3, $affected, 'insertMultiple() should return number of inserted rows'); + $this->assertEqualsCanonicalizing(['x', 'y'], $this->tagRepository->getAll()); + } + + public function testNoDuplicatesAreRetrievedAfterSubsequentCalls() + { + $affected = $this->tagRepository->insertMultiple(['x', 'y']); + $this->assertEquals(2, $affected, 'insertMultiple() should return number of inserted rows'); + $affected = $this->tagRepository->insertMultiple(['y', 'z']); + $this->assertEquals(2, $affected, 'insertMultiple() should return number of inserted rows'); + $this->assertEqualsCanonicalizing(['x', 'y', 'z'], $this->tagRepository->getAll()); + } + + public function testLastUsedIdIncreases() + { + $this->tagRepository->insertMultiple(['x']); + $this->tagRepository->getAll(); + $lastUsedId = $this->tagRepository->getLastUsedId(); + $this->tagRepository->insertMultiple(['y']); + $this->tagRepository->getAll(); + //TODO maybe throw exception if getAll has not been called before: + $this->assertEquals($lastUsedId + 1, $this->tagRepository->getLastUsedId()); + } + + public function testDeleteUpToId() + { + $this->tagRepository->insertMultiple(['x', 'y', 'z']); + $this->tagRepository->getAll(); + $lastUsedId = $this->tagRepository->getLastUsedId(); + $this->tagRepository->insertMultiple(['a', 'b', 'c']); + $affected = $this->tagRepository->deleteUpToId($lastUsedId); + $this->assertEquals(3, $affected, 'deleteUpToId() should return number of deleted rows'); + $this->assertEqualsCanonicalizing(['a', 'b', 'c'], $this->tagRepository->getAll()); + } + + /** + * Backport from PHPUnit 8 + * + * @param array $expected + * @param array $actual + */ + public static function assertEqualsCanonicalizing(array $expected, array $actual, string $message = '') + { + self::assertEquals($expected, $actual, $message, 0.0, 10, true); + } +} diff --git a/tests/Integration/ModuleTest.php b/tests/Integration/ModuleTest.php new file mode 100644 index 0000000..865824e --- /dev/null +++ b/tests/Integration/ModuleTest.php @@ -0,0 +1,44 @@ +objectManager->create(ModuleList::class); + return $moduleList; + } + protected function setUp() + { + $this->objectManager = ObjectManager::getInstance(); + } + public function testTheModuleIsRegistered() + { + $registrar = new ComponentRegistrar(); + $paths = $registrar->getPaths(ComponentRegistrar::MODULE); + $this->assertArrayHasKey(self::MODULE_NAME, $paths, 'Module should be registered'); + } + public function testTheModuleIsKnownAndEnabled() + { + $moduleList = $this->getTestModuleList(); + $this->assertTrue($moduleList->has(self::MODULE_NAME), 'Module should be enabled'); + } + +} \ No newline at end of file diff --git a/tests/Integration/PurgeCacheTest.php b/tests/Integration/PurgeCacheTest.php new file mode 100644 index 0000000..5caa888 --- /dev/null +++ b/tests/Integration/PurgeCacheTest.php @@ -0,0 +1,135 @@ +startMockServer(); + $this->createRequestLog(); + + $this->configureVarnishHost(); + $this->purgeCache = Bootstrap::getObjectManager()->get(PurgeCache::class); + } + + private function configureVarnishHost() + { + /** @var ObjectManager $objectManager */ + $objectManager = Bootstrap::getObjectManager(); + $deploymentConfig = new DeploymentConfig( + $objectManager->get(DeploymentConfig\Reader::class), + ['http_cache_hosts' => [['host' => '127.0.0.1', 'port' => self::MOCK_SERVER_PORT]]] + ); + $objectManager->addSharedInstance( + $deploymentConfig, + DeploymentConfig::class + ); + } + + protected function tearDown() + { + $this->stopMockServer(); + } + + public function testWebserver() + { + $this->assertEquals("OK\n", \file_get_contents('http://127.0.0.1:' . self::MOCK_SERVER_PORT . '/')); + } + + public function testPurgeRequestIsSentToVarnish() + { + $tagsPattern = 'XXX|YYY|ZZZZ'; + $result = $this->purgeCache->sendPurgeRequest($tagsPattern); + $this->assertTrue($result); + $this->assertEquals( + [ + [ + 'method' => 'PURGE', + 'headers' => ['Host' => ['127.0.0.1'], 'X-Magento-Tags-Pattern' => [$tagsPattern]], + ], + ], + $this->getRequestsFromLog() + ); + } + + private function startMockServer(): void + { + $objectManager = Bootstrap::getObjectManager(); + /** @var PhpExecutableFinder $phpExecutableFinder */ + $phpExecutableFinder = $objectManager->get(PhpExecutableFinder::class); + $mockServerCmd = $phpExecutableFinder->find() . ' ' . self::MOCK_SERVER_DIR . '/server.php'; + //the following needs Symfony Process >= 4.2.0 +// $this->mockServerProcess = Process::fromShellCommandline($mockServerCmd); + //so we use the old way to instantiate Process from string: + $this->mockServerProcess = new Process($mockServerCmd); + $this->mockServerProcess->start(); + //the following needs Symfony Process >= 4.2.0 +// $this->mockServerProcess->waitUntil( +// function($output) { +// return $output === 'Started'; +// } +// ); + // so we wait a second or two instead: + sleep(2); + } + + private function stopMockServer(): void + { + // issue: this only kills the parent shell script, not the PHP process (Symfony Process 4.1) +// $this->mockServerProcess->stop(); + // so we implemented a kill switch in the server: + $ch = \curl_init('http://127.0.0.1:8082/?kill=1'); + \curl_exec($ch); + } + + private function createRequestLog(): void + { + \file_put_contents(self::REQUEST_LOG_FILE, ''); + \chmod(self::REQUEST_LOG_FILE, 0666); + } + + private function getRequestsFromLog(): array + { + $requests = \array_map( + function (string $line): array { + return \json_decode($line, true); + }, + \file(self::REQUEST_LOG_FILE) + ); + return $requests; + } +} diff --git a/tests/Integration/TagRepositoryTest.php b/tests/Integration/TagRepositoryTest.php new file mode 100644 index 0000000..662baa0 --- /dev/null +++ b/tests/Integration/TagRepositoryTest.php @@ -0,0 +1,21 @@ +get(TagRepository::class); + } +} diff --git a/tests/Integration/VarnishMock/.gitignore b/tests/Integration/VarnishMock/.gitignore new file mode 100644 index 0000000..89267c3 --- /dev/null +++ b/tests/Integration/VarnishMock/.gitignore @@ -0,0 +1 @@ +/.requests.log \ No newline at end of file diff --git a/tests/Integration/VarnishMock/server.php b/tests/Integration/VarnishMock/server.php new file mode 100644 index 0000000..17a1dc9 --- /dev/null +++ b/tests/Integration/VarnishMock/server.php @@ -0,0 +1,38 @@ +getQueryParams()['kill'] ?? false) { + exit; + } + $requestJson = \json_encode( + [ + 'method' => $request->getMethod(), + 'headers' => $request->getHeaders() + ] + ); + \file_put_contents(__DIR__ . '/.requests.log', $requestJson . "\n", FILE_APPEND); + + return new \React\Http\Response( + 200, + array( + 'Content-Type' => 'text/plain' + ), + "OK\n" + ); +}); + +$socket = new \React\Socket\Server(8082, $loop); +$server->listen($socket); + +echo "Started"; + +$loop->run(); diff --git a/tests/Unit/FakeTagRepositoryTest.php b/tests/Unit/FakeTagRepositoryTest.php new file mode 100644 index 0000000..be3743a --- /dev/null +++ b/tests/Unit/FakeTagRepositoryTest.php @@ -0,0 +1,17 @@ + + + + ../../../vendor/integer-net/magento2-async-varnish/tests/Unit + + + + + + + + ../../src/app/code/* + + ../../src/app/code/*/*/Test + + + + + + + + + + + + + diff --git a/tests/src/FakeTagRepository.php b/tests/src/FakeTagRepository.php new file mode 100644 index 0000000..229283e --- /dev/null +++ b/tests/src/FakeTagRepository.php @@ -0,0 +1,57 @@ +tags = array_merge($this->tags, $tags); + return count($tags); + } + + public function deleteUpToId(int $maxId = 0): int + { + $deleted = 0; + foreach ($this->tags as $key => $tag) { + if ($key <= $maxId) { + unset($this->tags[$key]); + ++$deleted; + } + } + return $deleted; + } + + public function getAll(): array + { + return array_unique($this->tags); + } + + public function getLastUsedId(): int + { + return array_key_last($this->tags); + } + +} + +/* + * PHP 7.2 Polyfill + */ +if (!\function_exists('array_key_last')) { + function array_key_last(array $array) + { + if (empty($array)) { + return null; + } + + return key(array_slice($array, -1, 1, true)); + } +} \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy