diff --git a/.circleci/config.yml b/.circleci/config.yml index 6519b6c8..1fd7ae26 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -125,7 +125,7 @@ jobs: - check_mongodb_lib_version - run_test - test-replica-set: + test-replica-set-mongo: parameters: mongo_version: { type: string } docker: @@ -153,12 +153,16 @@ workflows: jobs: - lint - test: - name: test-php74 - php_version: php74 - mongo_version: 4.0.28 + mongo_version: "4.2" + matrix: + parameters: + php_version: + - php74 + - php84 + - php85 - test-multiple-stores - test-replica-set-mmap - - test-replica-set: + - test-replica-set-mongo: matrix: parameters: mongo_version: diff --git a/.gitignore b/.gitignore index ad315119..7921eae5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ tags atlassian-ide-plugin.xml composer.lock +docker-compose.override.yml profiler test-results .phpunit.result.cache diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 822972a2..14b568e1 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -41,4 +41,5 @@ ->ignoreVCSIgnored(true) ->ignoreDotFiles(false) ->notPath('rector.php') + ->notPath('test/unit/mongo/mocks') ); diff --git a/README.md b/README.md index 0cc7514a..d59b4859 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ Quickstart require_once("tripod.inc.php"); // Queue worker must register these event listeners -Resque_Event::listen('beforePerform', [\Tripod\Mongo\Jobs\JobBase::class, 'beforePerform']); -Resque_Event::listen('onFailure', [\Tripod\Mongo\Jobs\JobBase::class, 'onFailure']); +Resque\Event::listen('beforePerform', [\Tripod\Mongo\Jobs\JobBase::class, 'beforePerform']); +Resque\Event::listen('onFailure', [\Tripod\Mongo\Jobs\JobBase::class, 'onFailure']); \Tripod\Config::setConfig($conf); // set the config, usually read in as JSON from a file diff --git a/composer.json b/composer.json index c6a53d78..8ff0f797 100644 --- a/composer.json +++ b/composer.json @@ -21,20 +21,23 @@ "resque/php-resque": "Redis backed library for background jobs" }, "require": { - "php" : ">=7.4", + "php": ">=7.4", + "ext-mongodb": ">=1.7.0", "mongodb/mongodb": "*", - "monolog/monolog" : "~1.13", - "semsol/arc2": "2.2.6" + "psr/log": "*", + "semsol/arc2": "^2.2.6 || ^3.1.0" }, "require-dev": { "phpunit/phpunit": "^9.6.20", - "resque/php-resque": "v1.3.6", + "resque/php-resque": "v1.3.6 || dev-develop", "friendsofphp/php-cs-fixer": "^3.4", "perftools/php-profiler": "^1.2", - "phpstan/phpstan": "^2.1" + "phpstan/phpstan": "^2.1", + "monolog/monolog": "^2.11 || ^3.10" }, "autoload": { - "classmap": ["src/"] + "classmap": ["src/"], + "exclude-from-classmap": ["src/resque/compat.inc.php"] }, "autoload-dev": { "classmap": ["test/"] diff --git a/docker-compose.php-versions.yml b/docker-compose.php-versions.yml new file mode 100644 index 00000000..1ce89f5a --- /dev/null +++ b/docker-compose.php-versions.yml @@ -0,0 +1,26 @@ +# An override file to allow using separate vendor directories for each PHP version. +# Usage: +# 1. Create the directories and composer.lock files for each PHP version: +# mkdir -p docker/{php74,php84,php85}/vendor +# echo '{}' > docker/{php74,php84,php85}/composer.lock +# 2. Run the desired PHP version with the appropriate vendor directory: +# docker compose -f docker-compose.yml -f docker-compose.php-versions.yml run --rm -it php84 + +services: + php74: + volumes: + - ./docker/php74/vendor:/var/tripod-php/vendor + - ./docker/php74/composer.lock:/var/tripod-php/composer.lock + + php84: + volumes: + - ./docker/php84/vendor:/var/tripod-php/vendor + - ./docker/php84/composer.lock:/var/tripod-php/composer.lock + + php85: + volumes: + - ./docker/php85/vendor:/var/tripod-php/vendor + - ./docker/php85/composer.lock:/var/tripod-php/composer.lock + + mongodb2: + image: mongo:4.4 diff --git a/docker-compose.yml b/docker-compose.yml index 81a577f0..27f5b470 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,12 +11,26 @@ services: php74: build: context: ./docker - dockerfile: Dockerfile-php74 + dockerfile: php74.Dockerfile image: talis/tripod-php:php74-latest <<: *base-config + php84: + build: + context: ./docker + dockerfile: php84.Dockerfile + image: talis/tripod-php:php84-latest + <<: *base-config + + php85: + build: + context: ./docker + dockerfile: php85.Dockerfile + image: talis/tripod-php:php85-latest + <<: *base-config + mongodb: - image: mongo:3.6.23 + image: mongo:4.0 redis: image: valkey/valkey:8-alpine diff --git a/docker/Dockerfile-php74 b/docker/php74.Dockerfile similarity index 100% rename from docker/Dockerfile-php74 rename to docker/php74.Dockerfile diff --git a/docker/php84.Dockerfile b/docker/php84.Dockerfile new file mode 100644 index 00000000..400aef2a --- /dev/null +++ b/docker/php84.Dockerfile @@ -0,0 +1,18 @@ +FROM php:8.4.19-cli + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + git \ + unzip \ + zip \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -sLo /tmp/mongosh.deb https://downloads.mongodb.com/compass/mongodb-mongosh_2.6.0_amd64.deb \ + && dpkg -i /tmp/mongosh.deb \ + && rm /tmp/mongosh.deb + +COPY --from=mlocati/php-extension-installer:2.10.6 /usr/bin/install-php-extensions /usr/local/bin/ +COPY --from=composer:2.9.5 /usr/bin/composer /usr/local/bin/ + +RUN IPE_ICU_EN_ONLY=1 install-php-extensions pcntl mongodb-1.19.4 xhprof diff --git a/docker/php85.Dockerfile b/docker/php85.Dockerfile new file mode 100644 index 00000000..47574e61 --- /dev/null +++ b/docker/php85.Dockerfile @@ -0,0 +1,18 @@ +FROM php:8.5.4-cli + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + git \ + unzip \ + zip \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -sLo /tmp/mongosh.deb https://downloads.mongodb.com/compass/mongodb-mongosh_2.6.0_amd64.deb \ + && dpkg -i /tmp/mongosh.deb \ + && rm /tmp/mongosh.deb + +COPY --from=mlocati/php-extension-installer:2.10.6 /usr/bin/install-php-extensions /usr/local/bin/ +COPY --from=composer:2.9.5 /usr/bin/composer /usr/local/bin/ + +RUN IPE_ICU_EN_ONLY=1 install-php-extensions pcntl mongodb-2.2.1 xhprof diff --git a/phpstan.neon b/phpstan.neon index 951a7121..894e93de 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,15 +1,15 @@ parameters: level: 6 - phpVersion: - min: 70400 # PHP 7.4 - max: 80419 # PHP 8.4.19 + bootstrapFiles: + - src/resque/compat.inc.php tips: treatPhpDocTypesAsCertain: false ignoreErrors: - - '#Access to undefined constant MongoDB\\Driver\\ReadPreference\:\:RP_.*#' - - '#Call to an undefined method MongoDB\\Driver\\ReadPreference\:\:getMode\(\)#' - identifier: missingType.iterableValue reportUnmatched: false paths: - src - test + excludePaths: + - src/resque/compat.inc.php + - test/unit/mongo/mocks diff --git a/scripts/mongo/worker.inc.php b/scripts/mongo/worker.inc.php index 1ecedcf7..596565c8 100644 --- a/scripts/mongo/worker.inc.php +++ b/scripts/mongo/worker.inc.php @@ -9,8 +9,7 @@ require_once __DIR__ . '/../../src/tripod.inc.php'; // the global is necessary for Resque worker to send statements to -$logger = new Logger('TRIPOD-WORKER'); -$logger->pushHandler(new StreamHandler('php://stderr', LogLevel::WARNING)); // resque too chatty on NOTICE & INFO. YMMV +$logger = new Logger('TRIPOD-WORKER', [new StreamHandler('php://stderr', LogLevel::NOTICE)]); // this is so tripod itself uses the same logger -DriverBase::$logger = new Logger('TRIPOD-JOB', [new StreamHandler('php://stderr', LogLevel::DEBUG)]); +DriverBase::$logger = $logger; diff --git a/src/classes/ChangeSet.php b/src/classes/ChangeSet.php index cac45c1c..8be74883 100644 --- a/src/classes/ChangeSet.php +++ b/src/classes/ChangeSet.php @@ -78,7 +78,7 @@ public function __construct(array $a) $parser->parse(false, $a[$rdf]); $a[$rdf] = $parser->getSimpleIndex(0); } elseif ( - is_array($a[$rdf]) && isset($a[$rdf][0], $a[$rdf][0]['s']) + is_array($a[$rdf]) && isset($a[$rdf][0]['s']) ) { // triples array /** @var \ARC2_RDFSerializer $ser */ $ser = \ARC2::getTurtleSerializer(); diff --git a/src/classes/ExtendedGraph.php b/src/classes/ExtendedGraph.php index ff7d6689..1001c031 100644 --- a/src/classes/ExtendedGraph.php +++ b/src/classes/ExtendedGraph.php @@ -116,11 +116,6 @@ public function __construct($graph = null) } } - public function __destruct() - { - unset($this->_index); - } - /** * Map a portion of a URI to a short prefix for use when serialising the graph. * diff --git a/src/mongo/Config.php b/src/mongo/Config.php index 38e8c1d9..a9b5ee22 100644 --- a/src/mongo/Config.php +++ b/src/mongo/Config.php @@ -568,11 +568,9 @@ public function getSearchProviderClassName(string $storeName): ?string } /** - * @param int|string $readPreference - * * @throws ConfigException */ - public function getDatabase(string $storeName, ?string $dataSource = null, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Database + public function getDatabase(string $storeName, ?string $dataSource = null, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Database { if (!isset($this->dbConfig[$storeName])) { throw new ConfigException(sprintf("Store name '%s' not in configuration", $storeName)); @@ -590,13 +588,11 @@ public function getDatabase(string $storeName, ?string $dataSource = null, $read } /** - * @param int|string $readPreference - * * @throws ConfigException */ - public function getCollectionForCBD(string $storeName, string $podName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection + public function getCollectionForCBD(string $storeName, string $podName, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection { - if (isset($this->podConnections[$storeName], $this->podConnections[$storeName][$podName])) { + if (isset($this->podConnections[$storeName][$podName])) { return $this->getMongoCollection( $this->getDatabase($storeName, $this->podConnections[$storeName][$podName], $readPreference), $podName @@ -607,11 +603,9 @@ public function getCollectionForCBD(string $storeName, string $podName, $readPre } /** - * @param int|string $readPreference - * * @throws ConfigException */ - public function getCollectionForView(string $storeName, string $viewId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection + public function getCollectionForView(string $storeName, string $viewId, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection { if (!isset($this->viewSpecs[$storeName][$viewId])) { throw new ConfigException(sprintf("View id '%s' not in configuration for store '%s'", $viewId, $storeName)); @@ -626,11 +620,9 @@ public function getCollectionForView(string $storeName, string $viewId, $readPre } /** - * @param int|string $readPreference - * * @throws ConfigException */ - public function getCollectionForSearchDocument(string $storeName, string $searchDocumentId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection + public function getCollectionForSearchDocument(string $storeName, string $searchDocumentId, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection { if (!isset($this->searchDocSpecs[$storeName][$searchDocumentId])) { throw new ConfigException(sprintf("Search document id '%s' not in configuration for store '%s'", $searchDocumentId, $storeName)); @@ -645,11 +637,9 @@ public function getCollectionForSearchDocument(string $storeName, string $search } /** - * @param int|string $readPreference - * * @throws ConfigException */ - public function getCollectionForTable(string $storeName, string $tableId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection + public function getCollectionForTable(string $storeName, string $tableId, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection { if (!isset($this->tableSpecs[$storeName][$tableId])) { throw new ConfigException(sprintf("Table id '%s' not in configuration for store '%s'", $tableId, $storeName)); @@ -664,13 +654,11 @@ public function getCollectionForTable(string $storeName, string $tableId, $readP } /** - * @param int|string $readPreference - * * @return Collection[] * * @throws ConfigException */ - public function getCollectionsForTables(string $storeName, array $tables = [], $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): array + public function getCollectionsForTables(string $storeName, array $tables = [], string $readPreference = ReadPreference::PRIMARY_PREFERRED): array { if (!isset($this->tableSpecs[$storeName])) { return []; @@ -701,13 +689,11 @@ public function getCollectionsForTables(string $storeName, array $tables = [], $ } /** - * @param int|string $readPreference - * * @return Collection[] * * @throws ConfigException */ - public function getCollectionsForViews(string $storeName, array $views = [], $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): array + public function getCollectionsForViews(string $storeName, array $views = [], string $readPreference = ReadPreference::PRIMARY_PREFERRED): array { if (!isset($this->viewSpecs[$storeName])) { return []; @@ -738,13 +724,11 @@ public function getCollectionsForViews(string $storeName, array $views = [], $re } /** - * @param int|string $readPreference - * * @return Collection[] * * @throws ConfigException */ - public function getCollectionsForSearch(string $storeName, array $searchSpecIds = [], $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): array + public function getCollectionsForSearch(string $storeName, array $searchSpecIds = [], string $readPreference = ReadPreference::PRIMARY_PREFERRED): array { if (!isset($this->searchDocSpecs[$storeName])) { return []; @@ -774,10 +758,7 @@ public function getCollectionsForSearch(string $storeName, array $searchSpecIds return $collections; } - /** - * @param int|string $readPreference - */ - public function getCollectionForTTLCache(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection + public function getCollectionForTTLCache(string $storeName, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection { return $this->getMongoCollection( $this->getDatabase($storeName, $this->dbConfig[$storeName]['data_source'], $readPreference), @@ -785,10 +766,7 @@ public function getCollectionForTTLCache(string $storeName, $readPreference = Re ); } - /** - * @param int|string $readPreference - */ - public function getCollectionForLocks(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection + public function getCollectionForLocks(string $storeName, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection { return $this->getMongoCollection( $this->getDatabase($storeName, $this->dbConfig[$storeName]['data_source'], $readPreference), @@ -796,10 +774,7 @@ public function getCollectionForLocks(string $storeName, $readPreference = ReadP ); } - /** - * @param int|string $readPreference - */ - public function getCollectionForManualRollbackAudit(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection + public function getCollectionForManualRollbackAudit(string $storeName, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection { return $this->getMongoCollection( $this->getDatabase($storeName, $this->dbConfig[$storeName]['data_source'], $readPreference), @@ -807,10 +782,7 @@ public function getCollectionForManualRollbackAudit(string $storeName, $readPref ); } - /** - * @param int|string $readPreference - */ - public function getCollectionForJobGroups(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection + public function getCollectionForJobGroups(string $storeName, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection { return $this->getMongoCollection( $this->getDatabase($storeName, $this->dbConfig[$storeName]['data_source'], $readPreference), @@ -819,11 +791,9 @@ public function getCollectionForJobGroups(string $storeName, $readPreference = R } /** - * @param int|string $readPreference - * * @throws ConfigException */ - public function getTransactionLogDatabase($readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Database + public function getTransactionLogDatabase(string $readPreference = ReadPreference::PRIMARY_PREFERRED): Database { $client = $this->getConnectionForDataSource($this->tConfig['data_source']); $db = $client->selectDatabase($this->tConfig['database']); diff --git a/src/mongo/Driver.php b/src/mongo/Driver.php index 61741321..495f8472 100755 --- a/src/mongo/Driver.php +++ b/src/mongo/Driver.php @@ -41,7 +41,7 @@ class Driver extends DriverBase implements IDriver *
  • defaultContext: (string) to use where a specific default context is not defined. Default is Null
  • *
  • async: (array) determines the async behaviour of views, tables and search. For each of these array keys, if set to true, generation of these elements will be done asyncronously on save. Default is array(OP_VIEWS=>false,OP_TABLES=>true,OP_SEARCH=>true)
  • *
  • stat: this sets the stats object to use to record statistics around operations performed by Driver. Default is null
  • - *
  • readPreference: The Read preference to set for Mongo: Default is ReadPreference::RP_PRIMARY_PREFERRED
  • + *
  • readPreference: The Read preference to set for Mongo: Default is ReadPreference::PRIMARY_PREFERRED
  • *
  • retriesToGetLock: Retries to do when unable to get lock on a document, default is 20
  • */ public function __construct(string $podName, string $storeName, array $opts = []) @@ -50,7 +50,7 @@ public function __construct(string $podName, string $storeName, array $opts = [] 'defaultContext' => null, OP_ASYNC => [OP_VIEWS => false, OP_TABLES => true, OP_SEARCH => true], 'statsConfig' => [], - 'readPreference' => ReadPreference::RP_PRIMARY_PREFERRED, + 'readPreference' => ReadPreference::PRIMARY_PREFERRED, 'retriesToGetLock' => 20, ], $opts); @@ -321,7 +321,7 @@ public function getCount(array $query, ?string $groupBy = null, ?int $ttl = null $cursor = $this->collection->aggregate($ops); foreach ($cursor as $doc) { if (!is_array($doc[_ID_KEY])) { - $results[$doc[_ID_KEY]] = $doc['total']; + $results[$doc[_ID_KEY] ?? ''] = $doc['total']; } else { $results[implode(';', $doc[_ID_KEY])] = $doc['total']; } @@ -618,7 +618,7 @@ protected function getLabeller(): Labeller protected function getDataUpdater(): Updates { if ($this->updates === null) { - $readPreference = $this->collection->getReadPreference()->getMode(); + $readPreference = $this->collection->getReadPreference()->getModeString(); $opts = [ 'defaultContext' => $this->defaultContext, diff --git a/src/mongo/IConfigInstance.php b/src/mongo/IConfigInstance.php index 05fbf287..f83bd5bd 100644 --- a/src/mongo/IConfigInstance.php +++ b/src/mongo/IConfigInstance.php @@ -184,56 +184,56 @@ public function getSearchProviderClassName(string $storeName): ?string; /** * @param string $storeName Store (database) name * @param string|null $dataSource Database server identifier - * @param int|string $readPreference Mongo read preference + * @param string $readPreference Mongo read preference * * @throws ConfigException */ - public function getDatabase(string $storeName, ?string $dataSource = null, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Database; + public function getDatabase(string $storeName, ?string $dataSource = null, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Database; /** - * @param string $storeName Store (database) name - * @param string $podName Pod (collection) name - * @param int|string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param string $podName Pod (collection) name + * @param string $readPreference Mongo read preference * * @throws ConfigException */ - public function getCollectionForCBD(string $storeName, string $podName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection; + public function getCollectionForCBD(string $storeName, string $podName, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection; /** - * @param string $storeName Store (database) name - * @param string $viewId View spec ID - * @param int|string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param string $viewId View spec ID + * @param string $readPreference Mongo read preference * * @throws ConfigException */ - public function getCollectionForView(string $storeName, string $viewId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection; + public function getCollectionForView(string $storeName, string $viewId, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection; /** - * @param string $storeName Store (database) name - * @param string $searchDocumentId Search document spec ID - * @param int|string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param string $searchDocumentId Search document spec ID + * @param string $readPreference Mongo read preference * * @throws ConfigException */ public function getCollectionForSearchDocument( string $storeName, string $searchDocumentId, - $readPreference = ReadPreference::RP_PRIMARY_PREFERRED + string $readPreference = ReadPreference::PRIMARY_PREFERRED ): Collection; /** - * @param string $storeName Store (database) name - * @param string $tableId Table spec ID - * @param int|string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param string $tableId Table spec ID + * @param string $readPreference Mongo read preference * * @throws ConfigException */ - public function getCollectionForTable(string $storeName, string $tableId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection; + public function getCollectionForTable(string $storeName, string $tableId, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection; /** - * @param string $storeName Store (database) name - * @param array $tables Array of table spec IDs - * @param int|string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param array $tables Array of table spec IDs + * @param string $readPreference Mongo read preference * * @return Collection[] * @@ -242,13 +242,13 @@ public function getCollectionForTable(string $storeName, string $tableId, $readP public function getCollectionsForTables( string $storeName, array $tables = [], - $readPreference = ReadPreference::RP_PRIMARY_PREFERRED + string $readPreference = ReadPreference::PRIMARY_PREFERRED ): array; /** - * @param string $storeName Store (database) name - * @param array $views Array of view spec IDs - * @param int|string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param array $views Array of view spec IDs + * @param string $readPreference Mongo read preference * * @return Collection[] * @@ -257,13 +257,13 @@ public function getCollectionsForTables( public function getCollectionsForViews( string $storeName, array $views = [], - $readPreference = ReadPreference::RP_PRIMARY_PREFERRED + string $readPreference = ReadPreference::PRIMARY_PREFERRED ): array; /** - * @param string $storeName Store (database) name - * @param array $searchSpecIds Array of search document spec IDs - * @param int|string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param array $searchSpecIds Array of search document spec IDs + * @param string $readPreference Mongo read preference * * @return Collection[] * @@ -272,41 +272,38 @@ public function getCollectionsForViews( public function getCollectionsForSearch( string $storeName, array $searchSpecIds = [], - $readPreference = ReadPreference::RP_PRIMARY_PREFERRED + string $readPreference = ReadPreference::PRIMARY_PREFERRED ): array; /** - * @param string $storeName Store (database) name - * @param int|string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param string $readPreference Mongo read preference */ - public function getCollectionForTTLCache(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection; + public function getCollectionForTTLCache(string $storeName, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection; - /** - * @param int|string $readPreference - */ - public function getCollectionForLocks(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection; + public function getCollectionForLocks(string $storeName, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection; /** - * @param string $storeName Store (database) name - * @param int|string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param string $readPreference Mongo read preference */ public function getCollectionForManualRollbackAudit( string $storeName, - $readPreference = ReadPreference::RP_PRIMARY_PREFERRED + string $readPreference = ReadPreference::PRIMARY_PREFERRED ): Collection; /** - * @param string $storeName Store (database) name - * @param int|string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param string $readPreference Mongo read preference */ - public function getCollectionForJobGroups(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection; + public function getCollectionForJobGroups(string $storeName, string $readPreference = ReadPreference::PRIMARY_PREFERRED): Collection; /** - * @param int|string $readPreference Mongo read preference + * @param string $readPreference Mongo read preference * * @throws ConfigException */ - public function getTransactionLogDatabase($readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Database; + public function getTransactionLogDatabase(string $readPreference = ReadPreference::PRIMARY_PREFERRED): Database; /** * Return the maximum batch size for async operations. diff --git a/src/mongo/ImpactedSubject.php b/src/mongo/ImpactedSubject.php index a4a00044..bb647884 100644 --- a/src/mongo/ImpactedSubject.php +++ b/src/mongo/ImpactedSubject.php @@ -126,7 +126,7 @@ public function update(): void protected function getTripod(): Driver { return new Driver($this->getPodName(), $this->getStoreName(), [ - 'readPreference' => ReadPreference::RP_PRIMARY, + 'readPreference' => ReadPreference::PRIMARY, ]); } } diff --git a/src/mongo/base/DriverBase.php b/src/mongo/base/DriverBase.php index f165189b..f2ded43c 100644 --- a/src/mongo/base/DriverBase.php +++ b/src/mongo/base/DriverBase.php @@ -7,10 +7,10 @@ use MongoDB\Collection; use MongoDB\Database; use MongoDB\Driver\ReadPreference; -use Monolog\Logger; use Psr\Log\LoggerInterface; -use Psr\Log\LogLevel; +use Psr\Log\NullLogger; use Tripod\ITripodStat; +use Tripod\LoggerTrait; use Tripod\Mongo\Composites\Views; use Tripod\StatsD; use Tripod\Timer; @@ -18,6 +18,8 @@ abstract class DriverBase { + use LoggerTrait; + public static ?LoggerInterface $logger = null; protected string $storeName; @@ -34,15 +36,24 @@ abstract class DriverBase protected ?Collection $collection = null; - /** - * @var int|string - */ - protected $readPreference = ReadPreference::RP_PRIMARY_PREFERRED; + protected string $readPreference = ReadPreference::PRIMARY_PREFERRED; protected Labeller $labeller; protected IConfigInstance $config; + /** + * @codeCoverageIgnore + */ + public static function getLogger(): LoggerInterface + { + if (self::$logger == null) { + self::$logger = new NullLogger(); + } + + return self::$logger; + } + public function getStat(): ITripodStat { if ($this->stat == null) { @@ -80,64 +91,6 @@ public function getPodName(): string return $this->podName; } - /** - * @codeCoverageIgnore - */ - public function timingLog(string $type, ?array $params = null): void - { - $type = '[PID ' . getmypid() . '] ' . $type; - $this->log(LogLevel::DEBUG, $type, $params); - } - - /** - * @codeCoverageIgnore - */ - public function infoLog(string $message, ?array $params = null): void - { - $message = '[PID ' . getmypid() . '] ' . $message; - $this->log(LogLevel::INFO, $message, $params); - } - - /** - * @codeCoverageIgnore - */ - public function debugLog(string $message, ?array $params = null): void - { - $message = '[PID ' . getmypid() . '] ' . $message; - $this->log(LogLevel::DEBUG, $message, $params); - } - - /** - * @codeCoverageIgnore - */ - public function errorLog(string $message, ?array $params = null): void - { - $message = '[PID ' . getmypid() . '] ' . $message; - $this->log(LogLevel::ERROR, $message, $params); - } - - /** - * @codeCoverageIgnore - */ - public function warningLog(string $message, ?array $params = null): void - { - $message = '[PID ' . getmypid() . '] ' . $message; - $this->log(LogLevel::WARNING, $message, $params); - } - - /** - * @codeCoverageIgnore - */ - public static function getLogger(): LoggerInterface - { - if (self::$logger == null) { - $log = new Logger('TRIPOD'); - self::$logger = $log; - } - - return self::$logger; - } - /** * For mocking out the creation of stat objects. */ @@ -320,12 +273,4 @@ protected function getCollection(): Collection return $this->collection; } - - /** - * @codeCoverageIgnore - */ - private function log(string $level, string $message, ?array $params): void - { - self::getLogger()->log($level, $message, $params ?: []); - } } diff --git a/src/mongo/base/JobBase.php b/src/mongo/base/JobBase.php index 63af5ac1..dfd6cae8 100644 --- a/src/mongo/base/JobBase.php +++ b/src/mongo/base/JobBase.php @@ -5,45 +5,33 @@ namespace Tripod\Mongo\Jobs; use MongoDB\Driver\ReadPreference; +use Psr\Log\LoggerInterface; +use Resque\Job\Job; +use Resque\Job\Status; +use Resque\JobHandler; +use Resque\Resque; use Tripod\Config; use Tripod\Exceptions\Exception; use Tripod\Exceptions\JobException; use Tripod\ITripodConfigSerializer; use Tripod\ITripodStat; +use Tripod\LoggerTrait; use Tripod\Mongo\Driver; use Tripod\Mongo\DriverBase; use Tripod\Mongo\IConfigInstance; use Tripod\Timer; +use Tripod\TripodStatFactory; -abstract class JobBase extends DriverBase +abstract class JobBase extends Job { + use LoggerTrait; + public const TRIPOD_CONFIG_KEY = 'tripodConfig'; public const TRIPOD_CONFIG_GENERATOR = 'tripodConfigGenerator'; public const QUEUE_KEY = 'queue'; - /** - * Resque Job arguments, set by Resque_Job_Factory. - * - * @var array - */ - public $args; - - /** - * Resque Job queue, set by Resque_Job_Factory. - * - * @var string - */ - public $queue; - - /** - * Resque Job. - * - * @var \Resque_Job - */ - public $job; - /** * @var string[] */ @@ -54,6 +42,10 @@ abstract class JobBase extends DriverBase */ protected $configRequired = false; + protected ?ITripodStat $stat = null; + + protected array $statsConfig = []; + protected ?IConfigInstance $tripodConfig = null; protected ?Timer $timer = null; @@ -95,7 +87,7 @@ abstract public function perform(): void; /** * Called in every job prior to perform(). */ - public static function beforePerform(\Resque_Job $job): void + public static function beforePerform(JobHandler $job): void { $instance = $job->getInstance(); if (!$instance instanceof self) { @@ -109,9 +101,9 @@ public static function beforePerform(\Resque_Job $job): void * Resque event when a job failures. * * @param \Exception|\Throwable $e Exception or Error - * @param \Resque_Job $job The failed job + * @param JobHandler $job The failed job */ - public static function onFailure($e, \Resque_Job $job): void + public static function onFailure($e, JobHandler $job): void { $failedJob = $job->getInstance(); if (!$failedJob instanceof self) { @@ -122,13 +114,27 @@ public static function onFailure($e, \Resque_Job $job): void $failedJob->getStat()->increment($failedJob->getStatFailureIncrementKey()); } + public static function getLogger(): LoggerInterface + { + return DriverBase::getLogger(); + } + public function getStat(): ITripodStat { if ($this->statsConfig === []) { $this->getStatsConfig(); } - return parent::getStat(); + if ($this->stat == null) { + $this->setStat($this->getStatFromStatFactory()); + } + + return $this->stat; + } + + public function setStat(ITripodStat $stat): void + { + $this->stat = $stat; } /** @@ -143,6 +149,14 @@ public function getStatsConfig(): array return $this->statsConfig; } + /** + * For mocking out the creation of stat objects. + */ + protected function getStatFromStatFactory(): ITripodStat + { + return TripodStatFactory::create($this->statsConfig); + } + /** * Stat string for successful job timer. */ @@ -161,7 +175,7 @@ protected function getTripod(string $storeName, string $podName, array $opts = [ $opts, [ 'stat' => $this->getStat(), - 'readPreference' => ReadPreference::RP_PRIMARY, // important: make sure we always read from the primary + 'readPreference' => ReadPreference::PRIMARY, // important: make sure we always read from the primary ] ); if ($this->tripod == null) { @@ -267,7 +281,7 @@ protected function submitJob(string $queueName, string $class, array $data, int */ protected function enqueue(string $queueName, string $class, array $data) { - return \Resque::enqueue($queueName, $class, $data, true); + return Resque::enqueue($queueName, $class, $data, true); } /** @@ -275,7 +289,7 @@ protected function enqueue(string $queueName, string $class, array $data) */ protected function hasJobStatus(string $token): bool { - $status = new \Resque_Job_Status($token); + $status = new Status($token); return !empty($status->get()); } @@ -312,6 +326,14 @@ protected function deserializeConfig(array $config): IConfigInstance return Config::getInstance(); } + /** + * For mocking. + */ + protected function getConfigInstance(): IConfigInstance + { + return Config::getInstance(); + } + /** * Sets the Tripod config for the job. */ diff --git a/src/mongo/delegates/SearchDocuments.php b/src/mongo/delegates/SearchDocuments.php index 91ace9e0..f8c1ccab 100644 --- a/src/mongo/delegates/SearchDocuments.php +++ b/src/mongo/delegates/SearchDocuments.php @@ -14,10 +14,8 @@ class SearchDocuments extends DriverBase /** * Construct accepts actual objects rather than strings as this class is a delegate of * Tripod and should inherit connections set up there. - * - * @param int|string $readPreference */ - public function __construct(string $storeName, Collection $collection, string $defaultContext, ?ITripodStat $stat = null, $readPreference = ReadPreference::RP_PRIMARY) + public function __construct(string $storeName, Collection $collection, string $defaultContext, ?ITripodStat $stat = null, string $readPreference = ReadPreference::PRIMARY) { $this->labeller = new Labeller(); $this->storeName = $storeName; diff --git a/src/mongo/delegates/SearchIndexer.php b/src/mongo/delegates/SearchIndexer.php index 405ad2ea..ffbb17bd 100644 --- a/src/mongo/delegates/SearchIndexer.php +++ b/src/mongo/delegates/SearchIndexer.php @@ -23,11 +23,9 @@ class SearchIndexer extends CompositeBase private ?ISearchProvider $searchProvider = null; /** - * @param int|string $readPreference - * * @throws SearchException */ - public function __construct(Driver $tripod, $readPreference = ReadPreference::RP_PRIMARY) + public function __construct(Driver $tripod, string $readPreference = ReadPreference::PRIMARY) { $this->tripod = $tripod; $this->storeName = $tripod->getStoreName(); diff --git a/src/mongo/delegates/Tables.php b/src/mongo/delegates/Tables.php index 869a81f8..1d553203 100644 --- a/src/mongo/delegates/Tables.php +++ b/src/mongo/delegates/Tables.php @@ -68,15 +68,13 @@ class Tables extends CompositeBase /** * Construct accepts actual objects rather than strings as this class is a delegate of * Tripod and should inherit connections set up there. - * - * @param int|string $readPreference */ public function __construct( string $storeName, Collection $collection, ?string $defaultContext, ?ITripodStat $stat = null, - $readPreference = ReadPreference::RP_PRIMARY + string $readPreference = ReadPreference::PRIMARY ) { $this->labeller = new Labeller(); $this->storeName = $storeName; diff --git a/src/mongo/delegates/Updates.php b/src/mongo/delegates/Updates.php index 3c668fd2..7ba365ce 100644 --- a/src/mongo/delegates/Updates.php +++ b/src/mongo/delegates/Updates.php @@ -40,14 +40,14 @@ class Updates extends DriverBase private ?TransactionLog $transactionLog = null; /** - * @var int|string the original read preference gets stored here when changing for a write + * @var string the original read preference gets stored here when changing for a write */ - private $originalCollectionReadPreference = ''; + private string $originalCollectionReadPreference = ''; /** - * @var int|string the original read preference gets stored here when changing for a write + * @var string the original read preference gets stored here when changing for a write */ - private $originalDbReadPreference = ''; + private string $originalDbReadPreference = ''; private int $retriesToGetLock; @@ -74,7 +74,7 @@ public function __construct(Driver $tripod, array $opts = []) 'defaultContext' => null, OP_ASYNC => [OP_VIEWS => false, OP_TABLES => true, OP_SEARCH => true], 'stat' => null, - 'readPreference' => ReadPreference::RP_PRIMARY_PREFERRED, + 'readPreference' => ReadPreference::PRIMARY_PREFERRED, 'retriesToGetLock' => 20, ], $opts); $this->readPreference = $opts['readPreference']; @@ -396,23 +396,23 @@ protected function setReadPreferenceToPrimary(): void /** @var ReadPreference $dbReadPref */ $dbReadPref = $this->getDatabase()->getReadPreference(); - $dbPref = $dbReadPref->getMode(); + $dbPref = $dbReadPref->getModeString(); $dbTagsets = $dbReadPref->getTagsets(); - $this->originalDbReadPreference = $this->db->getReadPreference()->getMode(); - if ($dbPref !== ReadPreference::RP_PRIMARY) { - $this->db = $this->db->withOptions(['readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY, $dbTagsets)]); + $this->originalDbReadPreference = $this->db->getReadPreference()->getModeString(); + if ($dbPref !== ReadPreference::PRIMARY) { + $this->db = $this->db->withOptions(['readPreference' => new ReadPreference(ReadPreference::PRIMARY, $dbTagsets)]); } /** @var ReadPreference $collReadPref */ $collReadPref = $this->getCollection()->getReadPreference(); - $collPref = $collReadPref->getMode(); + $collPref = $collReadPref->getModeString(); $collTagsets = $collReadPref->getTagsets(); // Set collection preference $this->originalCollectionReadPreference = $collPref; - if ($collPref !== ReadPreference::RP_PRIMARY) { - $this->collection = $this->collection->withOptions(['readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY, $collTagsets)]); + if ($collPref !== ReadPreference::PRIMARY) { + $this->collection = $this->collection->withOptions(['readPreference' => new ReadPreference(ReadPreference::PRIMARY, $collTagsets)]); } } @@ -422,7 +422,7 @@ protected function setReadPreferenceToPrimary(): void protected function resetOriginalReadPreference(): void { $dbReadPref = $this->db->getReadPreference(); - if ($this->originalDbReadPreference !== $dbReadPref->getMode()) { + if ($this->originalDbReadPreference !== $dbReadPref->getModeString()) { $pref = $this->originalDbReadPreference ?: $this->readPreference; $dbTagsets = $dbReadPref->getTagsets(); @@ -433,7 +433,7 @@ protected function resetOriginalReadPreference(): void // Reset collection object $collReadPref = $this->getCollection()->getReadPreference(); - if ($this->originalCollectionReadPreference !== $collReadPref->getMode()) { + if ($this->originalCollectionReadPreference !== $collReadPref->getModeString()) { $pref = $this->originalCollectionReadPreference ?: $this->readPreference; $collTagsets = $collReadPref->getTagsets(); $this->collection = $this->collection->withOptions([ diff --git a/src/mongo/delegates/Views.php b/src/mongo/delegates/Views.php index 0955a56f..e445c2d5 100644 --- a/src/mongo/delegates/Views.php +++ b/src/mongo/delegates/Views.php @@ -23,10 +23,8 @@ class Views extends CompositeBase /** * Construct accepts actual objects rather than strings as this class is a delegate of * Tripod and should inherit connections set up there. - * - * @param int|string $readPreference */ - public function __construct(string $storeName, Collection $collection, ?string $defaultContext, ?ITripodStat $stat = null, $readPreference = ReadPreference::RP_PRIMARY) + public function __construct(string $storeName, Collection $collection, ?string $defaultContext, ?ITripodStat $stat = null, string $readPreference = ReadPreference::PRIMARY) { $this->storeName = $storeName; $this->labeller = new Labeller(); @@ -671,7 +669,7 @@ protected function extractProperties(array $source, array $viewSpec, string $fro $obj[$p] = $source[$p]; } - if (isset($source[$p], $source[$p][$i])) { + if (isset($source[$p][$i])) { if (!isset($obj[$p])) { $obj[$p] = []; } @@ -686,7 +684,7 @@ protected function extractProperties(array $source, array $viewSpec, string $fro } } else { foreach ($source as $p => $val) { - if (isset($viewSpec['joins'], $viewSpec['joins'][$p], $viewSpec['joins'][$p]['maxJoins'])) { + if (isset($viewSpec['joins'][$p]['maxJoins'])) { // todo: refactor with above (extract method) // only include up to maxJoins for ($i = 0; $i < $viewSpec['joins'][$p]['maxJoins']; $i++) { @@ -695,7 +693,7 @@ protected function extractProperties(array $source, array $viewSpec, string $fro } if ($val && isset($val[$i])) { - if (!$obj[$p]) { + if (!isset($obj[$p])) { $obj[$p] = []; } diff --git a/src/resque/compat.inc.php b/src/resque/compat.inc.php new file mode 100644 index 00000000..20dbf9f2 --- /dev/null +++ b/src/resque/compat.inc.php @@ -0,0 +1,86 @@ +log(LogLevel::DEBUG, $type, $params); + } + + /** + * @codeCoverageIgnore + */ + public function infoLog(string $message, ?array $params = null): void + { + $message = '[PID ' . getmypid() . '] ' . $message; + $this->log(LogLevel::INFO, $message, $params); + } + + /** + * @codeCoverageIgnore + */ + public function debugLog(string $message, ?array $params = null): void + { + $message = '[PID ' . getmypid() . '] ' . $message; + $this->log(LogLevel::DEBUG, $message, $params); + } + + /** + * @codeCoverageIgnore + */ + public function errorLog(string $message, ?array $params = null): void + { + $message = '[PID ' . getmypid() . '] ' . $message; + $this->log(LogLevel::ERROR, $message, $params); + } + + /** + * @codeCoverageIgnore + */ + public function warningLog(string $message, ?array $params = null): void + { + $message = '[PID ' . getmypid() . '] ' . $message; + $this->log(LogLevel::WARNING, $message, $params); + } + + /** + * @codeCoverageIgnore + */ + private function log(string $level, string $message, ?array $params): void + { + self::getLogger()->log($level, $message, $params ?: []); + } +} diff --git a/src/tripod.inc.php b/src/tripod.inc.php index 75d0a1d8..14e1dc97 100644 --- a/src/tripod.inc.php +++ b/src/tripod.inc.php @@ -7,8 +7,9 @@ } require_once TRIPOD_DIR . '/mongo/MongoTripodConstants.php'; +require_once TRIPOD_DIR . '/resque/compat.inc.php'; -Resque::setBackend(Config::getResqueServer()); +Resque\Resque::setBackend(Config::getResqueServer()); define('RDF_TYPE', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'); define('RDF_SUBJECT', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#subject'); diff --git a/test/bootstrap.php b/test/bootstrap.php index 14962e0f..c0dcd354 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -1,7 +1,7 @@ pushHandler(new NullHandler()); -DriverBase::$logger = $log; +$logger = new NullLogger(); +DriverBase::$logger = $logger; diff --git a/test/unit/ExtendedGraphTest.php b/test/unit/ExtendedGraphTest.php index dbba091c..9832f52f 100644 --- a/test/unit/ExtendedGraphTest.php +++ b/test/unit/ExtendedGraphTest.php @@ -29,6 +29,8 @@ public function testAddValidValueToLiteralResultsInTriple($value): void public function addValidValueToLiteralResultsInTriple_Provider(): iterable { yield ['String']; + yield ['']; + yield ['0']; yield [1]; yield [1.2]; yield [true]; @@ -63,7 +65,7 @@ public function addInvalidValueToLiteralResultsInNoTriple_Provider(): iterable */ public function testAddInvalidSubjectToLiteralThrowsException($value): void { - $this->expectExceptionMessageMatches('/^(The subject is invalid|Argument 1.+must be of the type string.+)$/'); + $this->expectExceptionMessageMatches('/^The subject is invalid$|Argument #?1.+must be of (the )?type string.+/'); $graph = new ExtendedGraph(); $graph->add_resource_triple($value, 'http://some/predicate', 'http://someplace.com'); @@ -72,6 +74,7 @@ public function testAddInvalidSubjectToLiteralThrowsException($value): void public function addInvalidSubjectToLiteralResultsInNoTriple_Provider(): iterable { yield ['']; + yield ['0']; yield [1]; yield [1.2]; yield [true]; @@ -88,7 +91,7 @@ public function addInvalidSubjectToLiteralResultsInNoTriple_Provider(): iterable */ public function testAddInvalidPredicateToLiteralThrowsException($value): void { - $this->expectExceptionMessageMatches('/^(The predicate is invalid|Argument 2.+must be of the type string.+)$/'); + $this->expectExceptionMessageMatches('/^The predicate is invalid$|Argument #?2.+must be of (the )?type string.+/'); $graph = new ExtendedGraph(); $graph->add_resource_triple('http://some/subject/1', $value, 'http://someplace.com'); @@ -97,6 +100,7 @@ public function testAddInvalidPredicateToLiteralThrowsException($value): void public function addInvalidPredicateToLiteralResultsInNoTriple_Provider(): iterable { yield ['']; + yield ['0']; yield [1]; yield [1.2]; yield [true]; @@ -158,7 +162,7 @@ public function addInvalidValueToResourceResultsInNoTriple_Provider(): iterable */ public function testAddInvalidSubjectToResourceThrowsException($value): void { - $this->expectExceptionMessageMatches('/^(The subject is invalid|Argument 1.+must be of the type string.+)$/'); + $this->expectExceptionMessageMatches('/^The subject is invalid$|Argument #?1.+must be of (the )?type string.+/'); $graph = new ExtendedGraph(); $graph->add_resource_triple($value, 'http://some/predicate', 'http://someplace.com'); @@ -167,6 +171,7 @@ public function testAddInvalidSubjectToResourceThrowsException($value): void public function addInvalidSubjectToResourceResultsInNoTriple_Provider(): iterable { yield ['']; + yield ['0']; yield [1]; yield [1.2]; yield [true]; @@ -183,7 +188,7 @@ public function addInvalidSubjectToResourceResultsInNoTriple_Provider(): iterabl */ public function testAddInvalidPredicateToResourceThrowsException($value): void { - $this->expectExceptionMessageMatches('/^(The predicate is invalid|Argument 2.+must be of the type string.+)$/'); + $this->expectExceptionMessageMatches('/^The predicate is invalid$|Argument #?2.+must be of (the )?type string.+/'); $graph = new ExtendedGraph(); $graph->add_resource_triple('http://some/subject/1', $value, 'http://someplace.com'); @@ -192,6 +197,7 @@ public function testAddInvalidPredicateToResourceThrowsException($value): void public function addInvalidPredicateToResourceResultsInNoTriple_Provider(): iterable { yield ['']; + yield ['0']; yield [1]; yield [1.2]; yield [true]; diff --git a/test/unit/mongo/ApplyOperationTest.php b/test/unit/mongo/ApplyOperationTest.php index 161082cc..6b225414 100644 --- a/test/unit/mongo/ApplyOperationTest.php +++ b/test/unit/mongo/ApplyOperationTest.php @@ -4,6 +4,7 @@ use MongoDB\BSON\ObjectId; use MongoDB\BSON\UTCDateTime; +use Resque\JobHandler; use Tripod\Config; use Tripod\Exceptions\JobException; use Tripod\Mongo\Composites\SearchIndexer; @@ -25,7 +26,7 @@ public function testMandatoryArgTripodConfig(): void unset($this->args['tripodConfig']); $job = new ApplyOperation(); $job->args = $this->args; - $job->job = new Resque_Job('queue', ['id' => uniqid()]); + $job->job = new JobHandler('queue', ['id' => uniqid()]); $this->expectException(Exception::class); $this->expectExceptionMessage('Argument tripodConfig or tripodConfigGenerator was not present in supplied job args for job Tripod\Mongo\Jobs\ApplyOperation'); $this->performJob($job); @@ -37,7 +38,7 @@ public function testMandatoryArgSubject(): void unset($this->args['subjects']); $job = new ApplyOperation(); $job->args = $this->args; - $job->job = new Resque_Job('queue', ['id' => uniqid()]); + $job->job = new JobHandler('queue', ['id' => uniqid()]); $this->expectException(Exception::class); $this->expectExceptionMessage('Argument subjects was not present in supplied job args for job Tripod\Mongo\Jobs\ApplyOperation'); $this->performJob($job); @@ -51,7 +52,7 @@ public function testApplyViewOperation(): void ->getMock(); $applyOperation->args = $this->args; - $applyOperation->job = new Resque_Job('queue', ['id' => uniqid()]); + $applyOperation->job = new JobHandler('queue', ['id' => uniqid()]); $statMock = $this->getMockStat( $this->args['statsConfig']['config']['host'], @@ -129,7 +130,7 @@ public function testApplyViewOperationDecrementsJobGroupForBatchOperations(): vo ->getMock(); $applyOperation->args = $this->args; - $applyOperation->job = new Resque_Job('queue', ['id' => uniqid()]); + $applyOperation->job = new JobHandler('queue', ['id' => uniqid()]); $jobTrackerId = new ObjectId(); $applyOperation->args[ApplyOperation::TRACKING_KEY] = $jobTrackerId->__toString(); @@ -231,7 +232,7 @@ public function testApplyViewOperationCleanupIfAllGroupJobsComplete(): void ->getMock(); $applyOperation->args = $this->args; - $applyOperation->job = new Resque_Job('queue', ['id' => uniqid()]); + $applyOperation->job = new JobHandler('queue', ['id' => uniqid()]); $jobTrackerId = new ObjectId(); $applyOperation->args[ApplyOperation::TRACKING_KEY] = $jobTrackerId->__toString(); @@ -358,7 +359,7 @@ public function testApplyTableOperation(): void $this->args['subjects'] = [$impactedSubject->toArray()]; $applyOperation->args = $this->args; - $applyOperation->job = new Resque_Job('queue', ['id' => uniqid()]); + $applyOperation->job = new JobHandler('queue', ['id' => uniqid()]); $subject = $this->getMockBuilder(ImpactedSubject::class) ->onlyMethods(['getTripod']) @@ -436,7 +437,7 @@ public function testApplyTableOperationDecrementsJobGroupForBatchOperations(): v ); $applyOperation->args = $this->args; - $applyOperation->job = new Resque_Job('queue', ['id' => uniqid()]); + $applyOperation->job = new JobHandler('queue', ['id' => uniqid()]); $jobTrackerId = new ObjectId(); $applyOperation->args[ApplyOperation::TRACKING_KEY] = $jobTrackerId->__toString(); @@ -539,7 +540,7 @@ public function testApplyTableOperationCleanupIfAllGroupJobsComplete(): void ); $applyOperation->args = $this->args; - $applyOperation->job = new Resque_Job('queue', ['id' => uniqid()]); + $applyOperation->job = new JobHandler('queue', ['id' => uniqid()]); $jobTrackerId = new ObjectId(); $applyOperation->args[ApplyOperation::TRACKING_KEY] = $jobTrackerId->__toString(); @@ -646,7 +647,7 @@ public function testApplySearchOperation(): void ); $applyOperation->args = $this->args; - $applyOperation->job = new Resque_Job('queue', ['id' => uniqid()]); + $applyOperation->job = new JobHandler('queue', ['id' => uniqid()]); $subject = $this->getMockBuilder(ImpactedSubject::class) ->onlyMethods(['getTripod']) @@ -721,7 +722,7 @@ public function testApplySearchOperationDecrementsJobGroupForBatchOperations(): ); $applyOperation->args = $this->args; - $applyOperation->job = new Resque_Job('queue', ['id' => uniqid()]); + $applyOperation->job = new JobHandler('queue', ['id' => uniqid()]); $jobTrackerId = new ObjectId(); $applyOperation->args[ApplyOperation::TRACKING_KEY] = $jobTrackerId->__toString(); @@ -817,7 +818,7 @@ public function testApplySearchOperationCleanupIfAllGroupJobsComplete(): void ); $applyOperation->args = $this->args; - $applyOperation->job = new Resque_Job('queue', ['id' => uniqid()]); + $applyOperation->job = new JobHandler('queue', ['id' => uniqid()]); $jobTrackerId = new ObjectId(); $applyOperation->args[ApplyOperation::TRACKING_KEY] = $jobTrackerId->__toString(); diff --git a/test/unit/mongo/ConfigGeneratorTest.php b/test/unit/mongo/ConfigGeneratorTest.php index bd43f2a2..17e01f4e 100644 --- a/test/unit/mongo/ConfigGeneratorTest.php +++ b/test/unit/mongo/ConfigGeneratorTest.php @@ -2,11 +2,13 @@ declare(strict_types=1); +use Resque\JobHandler; use Tripod\Config; use Tripod\ExtendedGraph; use Tripod\ITripodConfigSerializer; use Tripod\Mongo\Composites\Views; use Tripod\Mongo\Driver; +use Tripod\Mongo\DriverBase; use Tripod\Mongo\ImpactedSubject; use Tripod\Mongo\Jobs\ApplyOperation; use Tripod\Mongo\Jobs\DiscoverImpactedSubjects; @@ -46,6 +48,15 @@ public function testSerializeConfig(): void $this->assertEquals($this->config, $instance->serialize()); } + public function testLoggerInstance(): void + { + $this->assertSame( + DriverBase::getLogger(), + Config::getInstance()::getLogger(), + 'Config instance should return the same logger instance as DriverBase' + ); + } + public function testConfigGeneratorsSerializedInDiscoverJobs(): void { $originalGraph = new ExtendedGraph(); @@ -150,7 +161,7 @@ public function testSerializedConfigGeneratorsSentToApplyJobs(): void ->setMockClassName('ApplyOperation_TestConfigGenerator') ->getMock(); $discoverJob->args = $jobArgs; - $discoverJob->job = new Resque_Job('discover_queue', ['id' => uniqid()]); + $discoverJob->job = new JobHandler('discover_queue', ['id' => uniqid()]); $discoverJob->expects($this->once())->method('getTripod')->willReturn($tripod); $discoverJob->expects($this->once())->method('getApplyOperation')->willReturn($applyJob); $configInstance = Config::getInstance(); diff --git a/test/unit/mongo/DiscoverImpactedSubjectsTest.php b/test/unit/mongo/DiscoverImpactedSubjectsTest.php index b7b1da2a..eb32265b 100644 --- a/test/unit/mongo/DiscoverImpactedSubjectsTest.php +++ b/test/unit/mongo/DiscoverImpactedSubjectsTest.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use Resque\JobHandler; use Tripod\Config; use Tripod\Mongo\Composites\SearchIndexer; use Tripod\Mongo\Composites\Tables; @@ -22,7 +23,7 @@ public function testMandatoryArgTripodConfig(): void unset($this->args['tripodConfig']); $job = new DiscoverImpactedSubjects(); $job->args = $this->args; - $job->job = new Resque_Job('queue', ['id' => uniqid()]); + $job->job = new JobHandler('queue', ['id' => uniqid()]); $this->expectException(Exception::class); $this->expectExceptionMessage('Argument tripodConfig or tripodConfigGenerator was not present in supplied job args for job Tripod\Mongo\Jobs\DiscoverImpactedSubjects'); $this->performJob($job); @@ -34,7 +35,7 @@ public function testMandatoryArgStoreName(): void unset($this->args['storeName']); $job = new DiscoverImpactedSubjects(); $job->args = $this->args; - $job->job = new Resque_Job('queue', ['id' => uniqid()]); + $job->job = new JobHandler('queue', ['id' => uniqid()]); $this->expectException(Exception::class); $this->expectExceptionMessage('Argument storeName was not present in supplied job args for job Tripod\Mongo\Jobs\DiscoverImpactedSubjects'); $this->performJob($job); @@ -46,7 +47,7 @@ public function testMandatoryArgPodName(): void unset($this->args['podName']); $job = new DiscoverImpactedSubjects(); $job->args = $this->args; - $job->job = new Resque_Job('queue', ['id' => uniqid()]); + $job->job = new JobHandler('queue', ['id' => uniqid()]); $this->expectException(Exception::class); $this->expectExceptionMessage('Argument podName was not present in supplied job args for job Tripod\Mongo\Jobs\DiscoverImpactedSubjects'); $this->performJob($job); @@ -58,7 +59,7 @@ public function testMandatoryArgChanges(): void unset($this->args['changes']); $job = new DiscoverImpactedSubjects(); $job->args = $this->args; - $job->job = new Resque_Job('queue', ['id' => uniqid()]); + $job->job = new JobHandler('queue', ['id' => uniqid()]); $this->expectException(Exception::class); $this->expectExceptionMessage('Argument changes was not present in supplied job args for job Tripod\Mongo\Jobs\DiscoverImpactedSubjects'); $this->performJob($job); @@ -70,7 +71,7 @@ public function testMandatoryArgOperations(): void unset($this->args['operations']); $job = new DiscoverImpactedSubjects(); $job->args = $this->args; - $job->job = new Resque_Job('queue', ['id' => uniqid()]); + $job->job = new JobHandler('queue', ['id' => uniqid()]); $this->expectException(Exception::class); $this->expectExceptionMessage('Argument operations was not present in supplied job args for job Tripod\Mongo\Jobs\DiscoverImpactedSubjects'); $this->performJob($job); @@ -82,7 +83,7 @@ public function testMandatoryArgContextAlias(): void unset($this->args['contextAlias']); $job = new DiscoverImpactedSubjects(); $job->args = $this->args; - $job->job = new Resque_Job('queue', ['id' => uniqid()]); + $job->job = new JobHandler('queue', ['id' => uniqid()]); $this->expectException(Exception::class); $this->expectExceptionMessage('Argument contextAlias was not present in supplied job args for job Tripod\Mongo\Jobs\DiscoverImpactedSubjects'); $this->performJob($job); @@ -148,12 +149,12 @@ public function testSubmitApplyOperationsJob(): void ->willReturn($tripod); $discoverImpactedSubjects->args = $this->args; - $discoverImpactedSubjects->job = new Resque_Job('queue', ['id' => uniqid()]); + $discoverImpactedSubjects->job = new JobHandler('queue', ['id' => uniqid()]); $applyOperation = $this->getMockBuilder(ApplyOperation::class) ->onlyMethods(['createJob']) ->getMock(); - $applyOperation->job = new Resque_Job('queue', ['id' => uniqid()]); + $applyOperation->job = new JobHandler('queue', ['id' => uniqid()]); $viewSubject = new ImpactedSubject( [ @@ -352,7 +353,7 @@ public function testManualQueueNamePersistsThroughJob(): void $args = $this->args; $args['queue'] = 'TRIPOD_TESTING_QUEUE_' . uniqid(); $discoverImpactedSubjects->args = $args; - $discoverImpactedSubjects->job = new Resque_Job('queue', ['id' => uniqid()]); + $discoverImpactedSubjects->job = new JobHandler('queue', ['id' => uniqid()]); $tripod->expects($this->exactly(3)) ->method('getComposite') @@ -517,7 +518,7 @@ public function testDiscoverOperationWillSubmitApplyOperationForDistinctQueues() $args = $this->args; $args['operations'] = [OP_TABLES]; $discoverImpactedSubjects->args = $args; - $discoverImpactedSubjects->job = new Resque_Job('queue', ['id' => uniqid()]); + $discoverImpactedSubjects->job = new JobHandler('queue', ['id' => uniqid()]); $tripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getComposite']) @@ -749,7 +750,7 @@ public function testManuallySpecifiedQueueWillOverrideQueuesDefinedInConfig(): v $args['operations'] = [OP_TABLES]; $args['queue'] = 'TRIPOD_TESTING_QUEUE_' . uniqid(); $discoverImpactedSubjects->args = $args; - $discoverImpactedSubjects->job = new Resque_Job('queue', ['id' => uniqid()]); + $discoverImpactedSubjects->job = new JobHandler('queue', ['id' => uniqid()]); $tripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getComposite']) diff --git a/test/unit/mongo/EnsureIndexesTest.php b/test/unit/mongo/EnsureIndexesTest.php index 015dd1e5..31e7892a 100644 --- a/test/unit/mongo/EnsureIndexesTest.php +++ b/test/unit/mongo/EnsureIndexesTest.php @@ -3,6 +3,7 @@ declare(strict_types=1); use PHPUnit\Framework\MockObject\MockObject; +use Resque\JobHandler; use Tripod\Config; use Tripod\Exceptions\JobException; use Tripod\Mongo\IndexUtils; @@ -40,7 +41,7 @@ public function testMandatoryArgs(string $argument, ?string $argumentName = null $job = new EnsureIndexes(); $job->args = $this->args; - $job->job = new Resque_Job('queue', ['id' => uniqid()]); + $job->job = new JobHandler('queue', ['id' => uniqid()]); unset($job->args[$argument]); $this->expectException(Exception::class); @@ -217,7 +218,7 @@ private function createMockJob(array $methods = ['getIndexUtils', 'submitJob', ' ->onlyMethods($methods) ->setMockClassName('MockEnsureIndexes') ->getMock(); - $mockEnsureIndexesJob->job = new Resque_Job('queue', ['id' => uniqid()]); + $mockEnsureIndexesJob->job = new JobHandler('queue', ['id' => uniqid()]); return $mockEnsureIndexesJob; } diff --git a/test/unit/mongo/JobBaseTest.php b/test/unit/mongo/JobBaseTest.php index 0203be85..f07a0a69 100644 --- a/test/unit/mongo/JobBaseTest.php +++ b/test/unit/mongo/JobBaseTest.php @@ -2,16 +2,27 @@ declare(strict_types=1); +use Resque\JobHandler; use Tripod\Config; +use Tripod\Mongo\DriverBase; use Tripod\Mongo\IConfigInstance; class JobBaseTest extends MongoTripodTestBase { + public function testLoggerInstance(): void + { + $this->assertSame( + DriverBase::getLogger(), + TestJobBase::getLogger(), + 'JobBase should return the same logger instance as DriverBase' + ); + } + public function testGetTripodConfig(): void { $job = new TestJobBase(); $job->args = $this->getArgs(); - $job->job = new Resque_Job('queue', ['id' => uniqid()]); + $job->job = new JobHandler('queue', ['id' => uniqid()]); $this->assertInstanceOf(IConfigInstance::class, $job->getTripodConfig()); } diff --git a/test/unit/mongo/MongoGraphTest.php b/test/unit/mongo/MongoGraphTest.php index 16921b4a..2b0e9187 100644 --- a/test/unit/mongo/MongoGraphTest.php +++ b/test/unit/mongo/MongoGraphTest.php @@ -132,6 +132,8 @@ public function testAddTripodArrayContainingValidLiteralValues($value): void public function addTripodArrayContainingValidLiteralValues_Provider(): iterable { yield ['A String']; + yield ['']; + yield ['0']; yield [1]; yield [1.2]; yield [true]; @@ -200,8 +202,9 @@ public function testAddTripodArrayContainingInvalidPredicates($value): void public function addTripodArrayContainingInvalidPredicates_Provider(): iterable { + yield ['']; + yield ['0']; yield [1]; - yield [1.2]; yield [true]; } @@ -255,6 +258,7 @@ public function testAddTripodArrayContainingInvalidSubject($value): void public function addTripodArrayContainingInvalidSubject_Provider(): iterable { yield ['']; + yield ['0']; yield [1]; yield [1.2]; yield [true]; @@ -318,6 +322,8 @@ public function addTripodArrayContainingInvalidResourceValues_Provider(): iterab { yield [1]; yield [1.2]; + yield ['']; + yield ['0']; yield [true]; yield [[]]; yield [null]; diff --git a/test/unit/mongo/MongoSearchProviderTest.php b/test/unit/mongo/MongoSearchProviderTest.php index 40192cf8..ebe4b8b0 100644 --- a/test/unit/mongo/MongoSearchProviderTest.php +++ b/test/unit/mongo/MongoSearchProviderTest.php @@ -372,7 +372,9 @@ public function testSearchIndexingIsRetriedOnDuplicateKeyError(): void $searchProviderReflection = new ReflectionClass(MongoSearchProvider::class); $searchProviderConfigProperty = $searchProviderReflection->getProperty('config'); - $searchProviderConfigProperty->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $searchProviderConfigProperty->setAccessible(true); + } $searchProviderConfigProperty->setValue($this->mongoSearchProvider, $configInstance); $this->mongoSearchProvider->indexDocument([ diff --git a/test/unit/mongo/MongoTransactionLogTest.php b/test/unit/mongo/MongoTransactionLogTest.php index 7e63d255..88c5f1be 100644 --- a/test/unit/mongo/MongoTransactionLogTest.php +++ b/test/unit/mongo/MongoTransactionLogTest.php @@ -626,7 +626,7 @@ public function testTransactionIsLoggedCorrectlyWhenSaveFails(): void ksort($expectedCBD); $this->assertEquals($expectedCBD, $actualCBD, 'CBD in transaction should match our expected value exactly'); // finally check that the actual error was logged in the transaction_log - $this->assertTrue(isset($transactionDocument['error'], $transactionDocument['error']['reason']) && isset($transactionDocument['error']['trace']), 'The error should be logged, both the message and a stack trace'); + $this->assertTrue(isset($transactionDocument['error']['reason'], $transactionDocument['error']['trace']), 'The error should be logged, both the message and a stack trace'); $this->assertEquals('exception thrown by mock test', $transactionDocument['error']['reason'], 'The transaction log should have logged the exception our test suite threw'); $this->assertNotEmpty($transactionDocument['error']['trace'], 'The transaction log have a non empty error trace'); } diff --git a/test/unit/mongo/MongoTripodConfigUnitTest.php b/test/unit/mongo/MongoTripodConfigUnitTest.php index b7813a43..f2759454 100644 --- a/test/unit/mongo/MongoTripodConfigUnitTest.php +++ b/test/unit/mongo/MongoTripodConfigUnitTest.php @@ -1010,8 +1010,8 @@ public function testCollectionReadPreferencesAreAppliedToDatabase(): void $mockConfig->expects($this->exactly(2)) ->method('getDatabase') ->withConsecutive( - ['tripod_php_testing', 'rs1', ReadPreference::RP_SECONDARY_PREFERRED], - ['tripod_php_testing', 'rs1', ReadPreference::RP_NEAREST] + ['tripod_php_testing', 'rs1', ReadPreference::SECONDARY_PREFERRED], + ['tripod_php_testing', 'rs1', ReadPreference::NEAREST] ) ->willReturnCallback(function () { $mongo = new Client(); @@ -1019,8 +1019,8 @@ public function testCollectionReadPreferencesAreAppliedToDatabase(): void return $mongo->selectDatabase('tripod_php_testing'); }); - $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::RP_SECONDARY_PREFERRED); - $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::RP_NEAREST); + $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::SECONDARY_PREFERRED); + $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::NEAREST); } public function testDataLoadedInConfiguredDataSource(): void @@ -1689,9 +1689,9 @@ public function testMongoConnectionNoExceptions(): void ->method('getMongoClient') ->with('mongodb://mongodb:27017/', ['connectTimeoutMS' => 20000]) ->willReturnCallback(fn (): Client => new Client()); - $mockConfig->getDatabase('tripod_php_testing', 'rs1', ReadPreference::RP_SECONDARY_PREFERRED); - $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::RP_SECONDARY_PREFERRED); - $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::RP_NEAREST); + $mockConfig->getDatabase('tripod_php_testing', 'rs1', ReadPreference::SECONDARY_PREFERRED); + $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::SECONDARY_PREFERRED); + $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::NEAREST); } public function testMongoConnectionExceptionThrown(): void @@ -1707,7 +1707,7 @@ public function testMongoConnectionExceptionThrown(): void ->with('mongodb://mongodb:27017/', ['connectTimeoutMS' => 20000]) ->willThrowException(new ConnectionTimeoutException('Exception thrown when connecting to Mongo')); - $mockConfig->getDatabase('tripod_php_testing', 'rs1', ReadPreference::RP_SECONDARY_PREFERRED); + $mockConfig->getDatabase('tripod_php_testing', 'rs1', ReadPreference::SECONDARY_PREFERRED); } public function testMongoConnectionNoExceptionThrownWhenConnectionThrowsSomeExceptions(): void @@ -1728,8 +1728,8 @@ public function testMongoConnectionNoExceptionThrownWhenConnectionThrowsSomeExce ) ); - $mockConfig->getDatabase('tripod_php_testing', 'rs1', ReadPreference::RP_SECONDARY_PREFERRED); - $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::RP_SECONDARY_PREFERRED); - $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::RP_NEAREST); + $mockConfig->getDatabase('tripod_php_testing', 'rs1', ReadPreference::SECONDARY_PREFERRED); + $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::SECONDARY_PREFERRED); + $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::NEAREST); } } diff --git a/test/unit/mongo/MongoTripodDriverTest.php b/test/unit/mongo/MongoTripodDriverTest.php index c141be4f..f0a02599 100755 --- a/test/unit/mongo/MongoTripodDriverTest.php +++ b/test/unit/mongo/MongoTripodDriverTest.php @@ -685,7 +685,7 @@ public function testReadPreferencesOverMultipleSaves(): void ->setConstructorArgs([ 'CBD_testing', 'tripod_php_testing', - ['defaultContext' => 'http://talisaspire.com/', 'readPreference' => ReadPreference::RP_SECONDARY_PREFERRED], + ['defaultContext' => 'http://talisaspire.com/', 'readPreference' => ReadPreference::SECONDARY_PREFERRED], ]) ->getMock(); @@ -704,7 +704,7 @@ public function testReadPreferencesOverMultipleSaves(): void ->willReturn($tripodUpdate); $expectedCollectionReadPreference = $tripodMock->getCollectionReadPreference(); - $this->assertEquals(ReadPreference::RP_SECONDARY_PREFERRED, $expectedCollectionReadPreference->getMode()); + $this->assertEquals(ReadPreference::SECONDARY_PREFERRED, $expectedCollectionReadPreference->getModeString()); // Assert that a simple save results in read preferences being restored $g = new MongoGraph(); diff --git a/test/unit/mongo/MongoTripodSearchIndexerTest.php b/test/unit/mongo/MongoTripodSearchIndexerTest.php index c8e444c5..da5f74de 100644 --- a/test/unit/mongo/MongoTripodSearchIndexerTest.php +++ b/test/unit/mongo/MongoTripodSearchIndexerTest.php @@ -14,6 +14,7 @@ use Tripod\Mongo\MongoGraph; use Tripod\Mongo\MongoSearchProvider; use Tripod\Mongo\Updates; +use Tripod\Test\Mongo\Mocks\Cursor as FakeCursor; class MongoTripodSearchIndexerTest extends MongoTripodTestBase { @@ -508,7 +509,7 @@ public function testBatchSearchDocumentsGeneration(): void $docs[] = ['_id' => ['r' => 'tenantLists:batch' . $i, 'c' => 'tenantContexts:DefaultGraph']]; } - $fakeCursor = new ArrayIterator($docs); + $fakeCursor = new FakeCursor($docs); $configInstance = $this->getMockBuilder(TripodTestConfig::class) ->onlyMethods(['getCollectionForCBD']) ->disableOriginalConstructor() diff --git a/test/unit/mongo/MongoTripodTablesTest.php b/test/unit/mongo/MongoTripodTablesTest.php index 3e4ffea5..b00b71d6 100644 --- a/test/unit/mongo/MongoTripodTablesTest.php +++ b/test/unit/mongo/MongoTripodTablesTest.php @@ -21,6 +21,7 @@ use Tripod\Mongo\MongoGraph; use Tripod\Mongo\TransactionLog; use Tripod\Mongo\Updates; +use Tripod\Test\Mongo\Mocks\Cursor as FakeCursor; class MongoTripodTablesTest extends MongoTripodTestBase { @@ -338,7 +339,7 @@ public function testBatchTableRowGeneration(): void $docs[] = ['_id' => ['r' => 'tenantLists:batch' . $i, 'c' => 'tenantContexts:DefaultGraph']]; } - $fakeCursor = new ArrayIterator($docs); + $fakeCursor = new FakeCursor($docs); $configInstance = $this->getMockBuilder(TripodTestConfig::class) ->onlyMethods(['getCollectionForTable', 'getCollectionForCBD']) ->disableOriginalConstructor() diff --git a/test/unit/mongo/MongoTripodViewsTest.php b/test/unit/mongo/MongoTripodViewsTest.php index 12b914f9..0d2f3443 100644 --- a/test/unit/mongo/MongoTripodViewsTest.php +++ b/test/unit/mongo/MongoTripodViewsTest.php @@ -19,6 +19,7 @@ use Tripod\Mongo\MongoGraph; use Tripod\Mongo\TransactionLog; use Tripod\Mongo\Updates; +use Tripod\Test\Mongo\Mocks\Cursor as FakeCursor; class MongoTripodViewsTest extends MongoTripodTestBase { @@ -1956,7 +1957,8 @@ public function testCursorNoExceptions(): void ->setConstructorArgs([new Manager(), 'db', 'view']) ->onlyMethods(['find']) ->getMock(); - $mockCursor = $this->getMockBuilder(ArrayIterator::class) + $mockCursor = $this->getMockBuilder(FakeCursor::class) + ->setConstructorArgs([[]]) ->onlyMethods(['rewind']) ->getMock(); @@ -2015,7 +2017,8 @@ public function testCursorExceptionThrown(): void ->setConstructorArgs([new Manager(), 'db', 'view']) ->onlyMethods(['findOne', 'find']) ->getMock(); - $mockCursor = $this->getMockBuilder(ArrayIterator::class) + $mockCursor = $this->getMockBuilder(FakeCursor::class) + ->setConstructorArgs([[]]) ->onlyMethods(['rewind']) ->getMock(); @@ -2075,7 +2078,8 @@ public function testCursorNoExceptionThrownWhenCursorThrowsSomeExceptions(): voi ->setConstructorArgs([new Manager(), 'db', 'view']) ->onlyMethods(['find', 'findOne']) ->getMock(); - $mockCursor = $this->getMockBuilder(ArrayIterator::class) + $mockCursor = $this->getMockBuilder(FakeCursor::class) + ->setConstructorArgs([[]]) ->onlyMethods(['rewind']) ->getMock(); @@ -2320,7 +2324,7 @@ public function testBatchViewGeneration(): void $docs[] = ['_id' => ['r' => 'tenantLists:batch' . $i, 'c' => 'tenantContexts:DefaultGraph']]; } - $fakeCursor = new ArrayIterator($docs); + $fakeCursor = new FakeCursor($docs); $configInstance = $this->getMockBuilder(TripodTestConfig::class) ->onlyMethods(['getCollectionForView', 'getCollectionForCBD']) ->disableOriginalConstructor() diff --git a/test/unit/mongo/ResqueJobTestBase.php b/test/unit/mongo/ResqueJobTestBase.php index 3d527305..045fe807 100644 --- a/test/unit/mongo/ResqueJobTestBase.php +++ b/test/unit/mongo/ResqueJobTestBase.php @@ -2,15 +2,19 @@ declare(strict_types=1); +use Resque\JobHandler; use Tripod\Mongo\Jobs\JobBase; class ResqueJobTestBase extends MongoTripodTestBase { protected function performJob(JobBase $job): void { - $mockJob = $this->getMockBuilder(Resque_Job::class) + $mockJob = $this->getMockBuilder(JobHandler::class) ->onlyMethods(['getInstance', 'getArguments']) - ->setConstructorArgs(['test', get_class($job), $job->args]) + ->setConstructorArgs(['test', [ + 'class' => get_class($job), + 'args' => [$job->args], + ]]) ->getMock(); $mockJob->expects($this->atLeastOnce()) ->method('getInstance') diff --git a/test/unit/mongo/mocks/Cursor.php b/test/unit/mongo/mocks/Cursor.php new file mode 100644 index 00000000..c933ccd3 --- /dev/null +++ b/test/unit/mongo/mocks/Cursor.php @@ -0,0 +1,11 @@ += 80000) { + require_once __DIR__ . '/CursorPhp80.php'; + class Cursor extends CursorPhp80 {} +} else { + require_once __DIR__ . '/CursorPhp74.php'; + class Cursor extends CursorPhp74 {} +} diff --git a/test/unit/mongo/mocks/CursorPhp74.php b/test/unit/mongo/mocks/CursorPhp74.php new file mode 100644 index 00000000..53191b74 --- /dev/null +++ b/test/unit/mongo/mocks/CursorPhp74.php @@ -0,0 +1,5 @@ +array = $array; + } + + #[\ReturnTypeWillChange] // in ext-mongodb <1.20.0 returns MongoDB\Driver\CursorId + public function getId(): Int64 + { + return new Int64(0); + } + + public function getServer(): Server + { + return (new Manager())->getServers()[0]; + } + + public function isDead(): bool + { + return false; + } + + public function setTypeMap(array $typemap): void + { + // noop + } + + public function toArray(): array + { + return $this->array; + } + + public function key(): ?int + { + return parent::key(); + } + + public function current(): array|object|null + { + return parent::current(); + } +}