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