diff --git a/.github/actions/run-wp-tests/action.yml b/.github/actions/run-wp-tests/action.yml index 2aa4f9ffcc..9e267979f0 100644 --- a/.github/actions/run-wp-tests/action.yml +++ b/.github/actions/run-wp-tests/action.yml @@ -61,7 +61,7 @@ runs: fi - name: Set up PHP - uses: shivammathur/setup-php@2.30.1 + uses: shivammathur/setup-php@2.30.2 with: coverage: ${{ steps.coverage.outputs.coverage }} ini-values: ${{ steps.coverage.outputs.ini }} @@ -126,7 +126,7 @@ runs: "${PHPUNIT}" ${OPTIONS} - name: Upload coverage report - uses: codecov/codecov-action@v4.1.0 + uses: codecov/codecov-action@v4.2.0 with: files: ${{ inputs.coverage-file }} flags: ${{ inputs.coverage-flags }} diff --git a/.github/workflows/changelog-summary-prod.yml b/.github/workflows/changelog-summary-prod.yml index 790629dc89..15490281e2 100644 --- a/.github/workflows/changelog-summary-prod.yml +++ b/.github/workflows/changelog-summary-prod.yml @@ -64,7 +64,7 @@ jobs: uses: actions/checkout@v4.1.2 - name: Setup PHP - uses: shivammathur/setup-php@2.30.1 + uses: shivammathur/setup-php@2.30.2 - name: Install uses: ramsey/composer-install@3.0.0 diff --git a/.github/workflows/changelog-summary-staging.yml b/.github/workflows/changelog-summary-staging.yml index 0e5e515624..6b07c72c67 100644 --- a/.github/workflows/changelog-summary-staging.yml +++ b/.github/workflows/changelog-summary-staging.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4.1.2 - name: Setup PHP - uses: shivammathur/setup-php@2.30.1 + uses: shivammathur/setup-php@2.30.2 - name: Install uses: ramsey/composer-install@3.0.0 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f233a51443..9b499b0084 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -32,10 +32,10 @@ jobs: uses: actions/checkout@v4.1.2 - name: Initialize CodeQL - uses: github/codeql-action/init@v3.24.9 + uses: github/codeql-action/init@v3.24.10 with: languages: ${{ matrix.language }} config-file: ./.github/codeql-config.yml - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3.24.9 + uses: github/codeql-action/analyze@v3.24.10 diff --git a/.github/workflows/core-tests.yml b/.github/workflows/core-tests.yml index 36c388542e..700fcc3eb7 100644 --- a/.github/workflows/core-tests.yml +++ b/.github/workflows/core-tests.yml @@ -67,7 +67,7 @@ jobs: cache-dependency-path: 'wordpress/package-lock.json' - name: Set up PHP - uses: shivammathur/setup-php@2.30.1 + uses: shivammathur/setup-php@2.30.2 with: php-version: 8.0 coverage: none @@ -97,11 +97,11 @@ jobs: - name: WordPress Docker container debug information run: | - docker-compose run --rm mysql mysql --version - docker-compose run --rm php php --version - docker-compose run --rm php php -m - docker-compose run --rm php php -i - docker-compose run --rm php locale -a + docker compose run --rm mysql mysql --version + docker compose run --rm php php --version + docker compose run --rm php php -m + docker compose run --rm php php -i + docker compose run --rm php locale -a working-directory: wordpress - name: Install WordPress @@ -122,6 +122,5 @@ jobs: - name: Run external HTTP tests run: | - sed -i 's/ test_multiple_location_headers/ disabled_test_multiple_location_headers/' tests/phpunit/tests/http/base.php node ./tools/local-env/scripts/docker.js run php ./vendor/bin/phpunit --verbose -c phpunit.xml.dist --group external-http working-directory: wordpress diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5bb170aebb..6bfa87fb82 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@v4.1.2 - name: Set up PHP - uses: shivammathur/setup-php@2.30.1 + uses: shivammathur/setup-php@2.30.2 with: coverage: none env: diff --git a/001-core/options-api.php b/001-core/options-api.php index c06cc6a998..b86f770c9b 100644 --- a/001-core/options-api.php +++ b/001-core/options-api.php @@ -39,8 +39,16 @@ function pre_wp_load_alloptions_protections( $pre_loaded_alloptions, $force_cach } // 3) Otherwise query the DB for fresh results. + if ( function_exists( 'wp_autoload_values_to_autoload' ) ) { + $values = wp_autoload_values_to_autoload(); + } else { + $values = [ 'yes' ]; + } + + /** @var string[] $values */ + $suppress = $wpdb->suppress_errors(); - $alloptions_db = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE autoload = 'yes'" ); + $alloptions_db = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE autoload IN ( '" . implode( "', '", esc_sql( $values ) ) . "' )" ); $wpdb->suppress_errors( $suppress ); $alloptions = []; diff --git a/__tests__/e2e/package-lock.json b/__tests__/e2e/package-lock.json index 1f7afe5bb5..a929d4c0fb 100644 --- a/__tests__/e2e/package-lock.json +++ b/__tests__/e2e/package-lock.json @@ -660,16 +660,46 @@ } }, "node_modules/@playwright/test": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.0.tgz", + "integrity": "sha512-Ebw0+MCqoYflop7wVKj711ccbNlrwTBCtjY5rlbiY9kHL2bCYxq+qltK6uPsVBGGAOb033H2VO0YobcQVxoW7Q==", + "dev": true, + "dependencies": { + "playwright": "1.43.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@playwright/test/node_modules/playwright": { "version": "1.42.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz", - "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", + "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", "dev": true, "dependencies": { - "playwright": "1.42.1" + "playwright-core": "1.42.1" }, "bin": { "playwright": "cli.js" }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/@playwright/test/node_modules/playwright-core": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", + "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, "engines": { "node": ">=16" } @@ -693,9 +723,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", - "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "version": "20.12.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.5.tgz", + "integrity": "sha512-BD+BjQ9LS/D8ST9p5uqBxghlN+S42iuNxjsUGjeZobe/ciXzk2qb1B6IXc6AnRLS+yFJRpN2IPEHMzwspfDJNw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -4075,12 +4105,12 @@ } }, "node_modules/playwright": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", - "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.0.tgz", + "integrity": "sha512-SiOKHbVjTSf6wHuGCbqrEyzlm6qvXcv7mENP+OZon1I07brfZLGdfWV0l/efAzVx7TF3Z45ov1gPEkku9q25YQ==", "dev": true, "dependencies": { - "playwright-core": "1.42.1" + "playwright-core": "1.43.0" }, "bin": { "playwright": "cli.js" @@ -4093,9 +4123,9 @@ } }, "node_modules/playwright-core": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", - "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz", + "integrity": "sha512-iWFjyBUH97+pUFiyTqSLd8cDMMOS0r2ZYz2qEsPjH8/bX++sbIJT35MSwKnp1r/OQBAqC5XO99xFbJ9XClhf4w==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -4819,9 +4849,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", + "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", "dev": true, "bin": { "tsc": "bin/tsc", diff --git a/composer.json b/composer.json index adc7ee69c1..79882887ac 100644 --- a/composer.json +++ b/composer.json @@ -1,13 +1,13 @@ { "require-dev": { - "phpunit/phpunit": "9.6.18", + "phpunit/phpunit": "9.6.19", "automattic/vipwpcs": "3.0.0", "phpcompatibility/phpcompatibility-wp": "2.1.4", "erusev/parsedown": "1.7.4", "dms/phpunit-arraysubset-asserts": "0.5.0", - "yoast/phpunit-polyfills": "2.0.0", - "johnpbloch/wordpress-core": "6.4.3", - "wp-phpunit/wp-phpunit": "6.4.2", + "yoast/phpunit-polyfills": "2.0.1", + "johnpbloch/wordpress-core": "6.5.0", + "wp-phpunit/wp-phpunit": "6.5.0", "wp-cli/wp-cli": "2.10.0" }, "config": { diff --git a/composer.lock b/composer.lock index b1379ff46d..35caf77321 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3a13feb34e9474d3c61b5fee68aa6875", + "content-hash": "e7435b2dec9c1e31339045d745b4e6af", "packages": [], "packages-dev": [ { @@ -305,16 +305,16 @@ }, { "name": "johnpbloch/wordpress-core", - "version": "6.4.3", + "version": "6.5.0", "source": { "type": "git", "url": "https://github.com/johnpbloch/wordpress-core.git", - "reference": "389a4e72f3e14a995f89fa4bc5a69c7cf75ae2a8" + "reference": "e36de3196c8ff7247bb2e654b8827c884a1c2bc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/johnpbloch/wordpress-core/zipball/389a4e72f3e14a995f89fa4bc5a69c7cf75ae2a8", - "reference": "389a4e72f3e14a995f89fa4bc5a69c7cf75ae2a8", + "url": "https://api.github.com/repos/johnpbloch/wordpress-core/zipball/e36de3196c8ff7247bb2e654b8827c884a1c2bc0", + "reference": "e36de3196c8ff7247bb2e654b8827c884a1c2bc0", "shasum": "" }, "require": { @@ -322,7 +322,7 @@ "php": ">=7.0.0" }, "provide": { - "wordpress/core-implementation": "6.4.3" + "wordpress/core-implementation": "6.5.0" }, "type": "wordpress-core", "notification-url": "https://packagist.org/downloads/", @@ -349,7 +349,7 @@ "source": "https://core.trac.wordpress.org/browser", "wiki": "https://codex.wordpress.org/" }, - "time": "2024-01-30T19:31:14+00:00" + "time": "2024-04-02T18:26:47+00:00" }, { "name": "mustache/mustache", @@ -1297,16 +1297,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.18", + "version": "9.6.19", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04" + "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04", - "reference": "32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8", + "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8", "shasum": "" }, "require": { @@ -1380,7 +1380,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.18" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19" }, "funding": [ { @@ -1396,7 +1396,7 @@ "type": "tidelift" } ], - "time": "2024-03-21T12:07:32+00:00" + "time": "2024-04-05T04:35:58+00:00" }, { "name": "sebastian/cli-parser", @@ -3011,16 +3011,16 @@ }, { "name": "wp-phpunit/wp-phpunit", - "version": "6.4.2", + "version": "6.5.0", "source": { "type": "git", "url": "https://github.com/wp-phpunit/wp-phpunit.git", - "reference": "aa3c8f5d1b7efc295fd2b37c7264d2356a8c1099" + "reference": "4368fd1dd37d0314cbaa9040be39d835616aeb17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/aa3c8f5d1b7efc295fd2b37c7264d2356a8c1099", - "reference": "aa3c8f5d1b7efc295fd2b37c7264d2356a8c1099", + "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/4368fd1dd37d0314cbaa9040be39d835616aeb17", + "reference": "4368fd1dd37d0314cbaa9040be39d835616aeb17", "shasum": "" }, "type": "library", @@ -3055,20 +3055,20 @@ "issues": "https://github.com/wp-phpunit/issues", "source": "https://github.com/wp-phpunit/wp-phpunit" }, - "time": "2023-12-07T00:50:08+00:00" + "time": "2024-04-03T00:33:03+00:00" }, { "name": "yoast/phpunit-polyfills", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "c758753e8f9dac251fed396a73c8305af3f17922" + "reference": "4a088f125c970d6d6ea52c927f96fe39b330d0f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/c758753e8f9dac251fed396a73c8305af3f17922", - "reference": "c758753e8f9dac251fed396a73c8305af3f17922", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/4a088f125c970d6d6ea52c927f96fe39b330d0f1", + "reference": "4a088f125c970d6d6ea52c927f96fe39b330d0f1", "shasum": "" }, "require": { @@ -3076,7 +3076,9 @@ "phpunit/phpunit": "^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0" }, "require-dev": { - "yoast/yoastcs": "^2.3.0" + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "yoast/yoastcs": "^3.1.0" }, "type": "library", "extra": { @@ -3113,9 +3115,10 @@ ], "support": { "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", + "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", "source": "https://github.com/Yoast/PHPUnit-Polyfills" }, - "time": "2023-06-06T20:28:24+00:00" + "time": "2024-04-05T16:36:44+00:00" } ], "aliases": [], diff --git a/files/class-vip-filesystem.php b/files/class-vip-filesystem.php index a2031341e9..ac51a04b76 100644 --- a/files/class-vip-filesystem.php +++ b/files/class-vip-filesystem.php @@ -100,7 +100,6 @@ private function add_filters() { add_filter( 'get_attached_file', [ $this, 'filter_get_attached_file' ], 20 ); add_filter( 'wp_generate_attachment_metadata', [ $this, 'filter_wp_generate_attachment_metadata' ], 10, 2 ); add_filter( 'wp_read_image_metadata', [ $this, 'filter_wp_read_image_metadata' ], 10, 2 ); - add_filter( 'font_dir', [ $this, 'filter_wp_font_dir' ], 10, 1 ); /** * The core's function recurse_dirsize would call to opendir() which is not supported by the @@ -125,7 +124,6 @@ private function remove_filters() { remove_filter( 'get_attached_file', [ $this, 'filter_get_attached_file' ], 20 ); remove_filter( 'wp_generate_attachment_metadata', [ $this, 'filter_wp_generate_attachment_metadata' ] ); remove_filter( 'wp_read_image_metadata', [ $this, 'filter_wp_read_image_metadata' ], 10, 2 ); - remove_filter( 'font_dir', [ $this, 'filter_wp_font_dir' ], 10, 1 ); remove_filter( 'pre_recurse_dirsize', '__return_zero' ); } @@ -463,18 +461,4 @@ public function filter_wp_read_image_metadata( $meta, $file ) { return $meta; } - - /** - * Changes the Font Library directory to work with the VIP Filesystem. - */ - public function filter_wp_font_dir( $defaults ) { - $upload_dir = wp_upload_dir(); - - $defaults['basedir'] = $upload_dir['basedir'] . '/fonts'; - $defaults['baseurl'] = $upload_dir['baseurl'] . '/fonts'; - $defaults['path'] = $defaults['basedir']; - $defaults['url'] = $defaults['baseurl']; - - return $defaults; - } } diff --git a/integrations/block-data-api.php b/integrations/block-data-api.php index 9531ee880d..15f81fd5a8 100644 --- a/integrations/block-data-api.php +++ b/integrations/block-data-api.php @@ -20,7 +20,7 @@ class BlockDataApiIntegration extends Integration { * * @var string */ - protected string $version = '1.1'; + protected string $version = '1.2'; /** * Returns `true` if `Block Data API` is already available e.g. via customer code. We will use diff --git a/package-lock.json b/package-lock.json index 32f5381a87..a62c5b9869 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2060,9 +2060,9 @@ } }, "node_modules/@csstools/color-helpers": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-4.0.0.tgz", - "integrity": "sha512-wjyXB22/h2OvxAr3jldPB7R7kjTUEzopvjitS8jWtyd8fN6xJ8vy1HnHu0ZNfEkqpBJgQ76Q+sBDshWcMvTa/w==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-4.1.0.tgz", + "integrity": "sha512-pWRKF6cDwget8HowIIf2MqEmqIca/cf8/jO4b3PRtUF5EfQXYMtBIKycXB4yXTCUmwLKOoRZAzh/hjnc7ywOIg==", "dev": true, "funding": [ { @@ -2102,9 +2102,9 @@ } }, "node_modules/@csstools/css-color-parser": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-1.6.2.tgz", - "integrity": "sha512-mlt0PomBlDXMGcbPAqCG36Fw35LZTtaSgCQCHEs4k8QTv1cUKe0rJDlFSJMHtqrgQiLC7LAAS9+s9kKQp2ou/Q==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-1.6.3.tgz", + "integrity": "sha512-pQPUPo32HW3/NuZxrwr3VJHE+vGqSTVI5gK4jGbuJ7eOFUrsTmZikXcVdInCVWOvuxK5xbCzwDWoTlZUCAKN+A==", "dev": true, "funding": [ { @@ -2117,7 +2117,7 @@ } ], "dependencies": { - "@csstools/color-helpers": "^4.0.0", + "@csstools/color-helpers": "^4.1.0", "@csstools/css-calc": "^1.2.0" }, "engines": { @@ -2193,9 +2193,9 @@ } }, "node_modules/@csstools/postcss-cascade-layers": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-4.0.3.tgz", - "integrity": "sha512-RbkQoOH23yGhWVetgBTwFgIOHEyU2tKMN7blTz/YAKKabR6tr9pP7mYS23Q9snFY2hr8WSaV8Le64KdM9BtUSA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-4.0.4.tgz", + "integrity": "sha512-MKErv8lpEwVmAcAwidY1Kfd3oWrh2Q14kxHs9xn26XzjP/PrcdngWq63lJsZeMlBY7o+WlEOeE+FP6zPzeY2uw==", "dev": true, "funding": [ { @@ -2208,7 +2208,7 @@ } ], "dependencies": { - "@csstools/selector-specificity": "^3.0.2", + "@csstools/selector-specificity": "^3.0.3", "postcss-selector-parser": "^6.0.13" }, "engines": { @@ -2219,9 +2219,9 @@ } }, "node_modules/@csstools/postcss-color-function": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-3.0.12.tgz", - "integrity": "sha512-amPGGDI4Xmgu7VN2ciKQe0pP/j5raaETT50nzbnkydp9FMw7imKxSUnXdVQU4NmRgpLKIc5Q7jox0MFhMBImIg==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-3.0.13.tgz", + "integrity": "sha512-gM24cIPU45HSPJ2zllz7VKjS1OKQS1sKOMI7Wsw8gFyXSGAGrxhYo++McylOqOXd8ecMaKxKQMUJqJVibvJYig==", "dev": true, "funding": [ { @@ -2234,10 +2234,10 @@ } ], "dependencies": { - "@csstools/css-color-parser": "^1.6.2", + "@csstools/css-color-parser": "^1.6.3", "@csstools/css-parser-algorithms": "^2.6.1", "@csstools/css-tokenizer": "^2.2.4", - "@csstools/postcss-progressive-custom-properties": "^3.1.1", + "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0" }, "engines": { @@ -2248,9 +2248,9 @@ } }, "node_modules/@csstools/postcss-color-mix-function": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-2.0.12.tgz", - "integrity": "sha512-qpAEGwVVqHSa88i3gLb43IMpT4/LyZEE8HzZylQKKXFVJ7XykXaORTmXySxyH6H+flT+NyCnutKG2fegCVyCug==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-2.0.13.tgz", + "integrity": "sha512-mD8IIfGVeWkN1H1wfCqYePOg4cDnVrOXm4P0OlYcvKriq6sImGCGShv/2D88q6s3iUlLXfUBES+DUjLVjDMhnw==", "dev": true, "funding": [ { @@ -2263,10 +2263,10 @@ } ], "dependencies": { - "@csstools/css-color-parser": "^1.6.2", + "@csstools/css-color-parser": "^1.6.3", "@csstools/css-parser-algorithms": "^2.6.1", "@csstools/css-tokenizer": "^2.2.4", - "@csstools/postcss-progressive-custom-properties": "^3.1.1", + "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0" }, "engines": { @@ -2330,9 +2330,9 @@ } }, "node_modules/@csstools/postcss-gamut-mapping": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-1.0.5.tgz", - "integrity": "sha512-AJ74/4nHXgghLWY4/ydEhu3mzwN8c56EjIGrJsoEhKaNuGBAOtUfE5qbkc9XQQ0G2FMhHggqE+9eRrApeK7ebQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-1.0.6.tgz", + "integrity": "sha512-qGFpHU9cRf9qqkbHh9cWMTlBtGi/ujPgP/znQdwkbB4TgDR1ddI5wRRrksBsx64sfoUSlIEd70bxXzD9FtfdLg==", "dev": true, "funding": [ { @@ -2345,7 +2345,7 @@ } ], "dependencies": { - "@csstools/css-color-parser": "^1.6.2", + "@csstools/css-color-parser": "^1.6.3", "@csstools/css-parser-algorithms": "^2.6.1", "@csstools/css-tokenizer": "^2.2.4" }, @@ -2357,9 +2357,9 @@ } }, "node_modules/@csstools/postcss-gradients-interpolation-method": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-4.0.13.tgz", - "integrity": "sha512-dBbyxs9g+mrIzmEH+UtrqJUmvcJB/60j0ijhBcVJMHCgl/rKjj8ey6r/pJOI0EhkVsckOu3Prc9AGzH88C+1pQ==", + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-4.0.14.tgz", + "integrity": "sha512-VMWC3xtpchHJoRBb/fs1gJR/5nHopX+0GwwmgdCI1DjROtfWUKIW0nv8occ922Gv0/Lk93XBtYBv8JttVBMZUQ==", "dev": true, "funding": [ { @@ -2372,10 +2372,10 @@ } ], "dependencies": { - "@csstools/css-color-parser": "^1.6.2", + "@csstools/css-color-parser": "^1.6.3", "@csstools/css-parser-algorithms": "^2.6.1", "@csstools/css-tokenizer": "^2.2.4", - "@csstools/postcss-progressive-custom-properties": "^3.1.1", + "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0" }, "engines": { @@ -2386,9 +2386,9 @@ } }, "node_modules/@csstools/postcss-hwb-function": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-3.0.11.tgz", - "integrity": "sha512-c36FtMFptwGn5CmsfdONA40IlWG2lHeoC/TDyED/7lwiTht5okxe6iLAa9t2LjBBo5AHQSHfeMvOASdXk/SHog==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-3.0.12.tgz", + "integrity": "sha512-90kIs+FsM6isAXLVoFHTTl4h0J6g1J1M6ahpIjAs6/k7a2A9FB/q+l0MHpLre0ZiPlBf2y3e1j4L+79vml7kJw==", "dev": true, "funding": [ { @@ -2401,10 +2401,10 @@ } ], "dependencies": { - "@csstools/css-color-parser": "^1.6.2", + "@csstools/css-color-parser": "^1.6.3", "@csstools/css-parser-algorithms": "^2.6.1", "@csstools/css-tokenizer": "^2.2.4", - "@csstools/postcss-progressive-custom-properties": "^3.1.1", + "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0" }, "engines": { @@ -2415,9 +2415,9 @@ } }, "node_modules/@csstools/postcss-ic-unit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-3.0.5.tgz", - "integrity": "sha512-9CriM/zvKXa/lDARlxs/MgeyKE6ZmmX4V77VLD7VUxKLVSt0Go3NCy/gRMbwGzxbrk3iaHFXnFbc2lNw+/7jcg==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-3.0.6.tgz", + "integrity": "sha512-fHaU9C/sZPauXMrzPitZ/xbACbvxbkPpHoUgB9Kw5evtsBWdVkVrajOyiT9qX7/c+G1yjApoQjP1fQatldsy9w==", "dev": true, "funding": [ { @@ -2430,7 +2430,7 @@ } ], "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^3.1.1", + "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0", "postcss-value-parser": "^4.2.0" }, @@ -2464,9 +2464,9 @@ } }, "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-4.0.5.tgz", - "integrity": "sha512-qG3MI7IN3KY9UwdaE9E7G7sFydscVW7nAj5OGwaBP9tQPEEVdxXTGI+l1ZW5EUpZFSj+u3q/22fH5+8HI72+Bg==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-4.0.6.tgz", + "integrity": "sha512-HilOhAsMpFheMYkuaREZx+CGa4hsG6kQdzwXSsuqKDFzYz2eIMP213+3dH/vUbPXaWrzqLKr8m3i0dgYPoh7vg==", "dev": true, "funding": [ { @@ -2479,7 +2479,7 @@ } ], "dependencies": { - "@csstools/selector-specificity": "^3.0.2", + "@csstools/selector-specificity": "^3.0.3", "postcss-selector-parser": "^6.0.13" }, "engines": { @@ -2490,9 +2490,9 @@ } }, "node_modules/@csstools/postcss-light-dark-function": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-1.0.1.tgz", - "integrity": "sha512-CJOcp+m7Njbu91HtYMMoYuZznsvNSpJtLiR/7BO8/bHTXYPiuAZfxunh7wXLkMbHd5dRBgAVAQZ+H4iFqrvWZw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-1.0.2.tgz", + "integrity": "sha512-9OUOKCXzYQFdvpRIz7vDucZiRupwFExDAk0YEPYCuKR1ZzQiqNUJSF7/OazjrNlwgt8lDWN9ADxmx5Iapyy6Aw==", "dev": true, "funding": [ { @@ -2507,7 +2507,7 @@ "dependencies": { "@csstools/css-parser-algorithms": "^2.6.1", "@csstools/css-tokenizer": "^2.2.4", - "@csstools/postcss-progressive-custom-properties": "^3.1.1", + "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0" }, "engines": { @@ -2741,9 +2741,9 @@ } }, "node_modules/@csstools/postcss-oklab-function": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-3.0.12.tgz", - "integrity": "sha512-RNitTHamFvUUh8x+MJuPd2tCekYexUrylGKfUoor5D2GGcgzY1WB6Bl3pIj9t8bAq5h/lcacKaB2wmvUOTfGgQ==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-3.0.13.tgz", + "integrity": "sha512-xbzMmukDFAwCt2+279io7ZiamZj87s6cnU3UgKB3G+NMpRX9A6uvN8xlnTLCe384hqg6hix5vlOmwkxqACb5pg==", "dev": true, "funding": [ { @@ -2756,10 +2756,10 @@ } ], "dependencies": { - "@csstools/css-color-parser": "^1.6.2", + "@csstools/css-color-parser": "^1.6.3", "@csstools/css-parser-algorithms": "^2.6.1", "@csstools/css-tokenizer": "^2.2.4", - "@csstools/postcss-progressive-custom-properties": "^3.1.1", + "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0" }, "engines": { @@ -2770,9 +2770,9 @@ } }, "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-3.1.1.tgz", - "integrity": "sha512-cx/bZgj+MK8SpRZNTu2zGeVFMCQfhsaeuDhukAhfA53yykvIXaTIwLi5shW9hfkvPrkqBeFoiRAzq/qogxeHTA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-3.2.0.tgz", + "integrity": "sha512-BZlirVxCRgKlE7yVme+Xvif72eTn1MYXj8oZ4Knb+jwaH4u3AN1DjbhM7j86RP5vvuAOexJ4JwfifYYKWMN/QQ==", "dev": true, "funding": [ { @@ -2795,9 +2795,9 @@ } }, "node_modules/@csstools/postcss-relative-color-syntax": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-2.0.12.tgz", - "integrity": "sha512-VreDGDgE634niwCytLtkoE5kRxfva7bnMzSoyok7Eh9VPYFOm8CK/oJXt9y3df71Bxc9PG4KC8RA3CxTknudnw==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-2.0.13.tgz", + "integrity": "sha512-mENWPNcHdiEYtjHFfZP9U1jNukQgFpSQ7wvTvwiadK3qgNBiSl0vMSinM9kKsGsJLTHQ0LEAqWLHurU52I4Jeg==", "dev": true, "funding": [ { @@ -2810,10 +2810,10 @@ } ], "dependencies": { - "@csstools/css-color-parser": "^1.6.2", + "@csstools/css-color-parser": "^1.6.3", "@csstools/css-parser-algorithms": "^2.6.1", "@csstools/css-tokenizer": "^2.2.4", - "@csstools/postcss-progressive-custom-properties": "^3.1.1", + "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0" }, "engines": { @@ -2876,9 +2876,9 @@ } }, "node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-3.0.4.tgz", - "integrity": "sha512-yUZmbnUemgQmja7SpOZeU45+P49wNEgQguRdyTktFkZsHf7Gof+ZIYfvF6Cm+LsU1PwSupy4yUeEKKjX5+k6cQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-3.0.5.tgz", + "integrity": "sha512-qKxXpD0TYINkUtWDN1RHdeWKtZCzEv5j3UMT/ZGqyY27icwCFw7iKO0bUeLSHjYFBqhurCWvoOsa9REqLdrNDw==", "dev": true, "funding": [ { @@ -2891,7 +2891,7 @@ } ], "dependencies": { - "@csstools/color-helpers": "^4.0.0", + "@csstools/color-helpers": "^4.1.0", "postcss-value-parser": "^4.2.0" }, "engines": { @@ -2973,9 +2973,9 @@ } }, "node_modules/@csstools/selector-specificity": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.2.tgz", - "integrity": "sha512-RpHaZ1h9LE7aALeQXmXrJkRG84ZxIsctEN2biEUmFyKpzFM3zZ35eUMcIzZFsw/2olQE6v69+esEqU2f1MKycg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.3.tgz", + "integrity": "sha512-KEPNw4+WW5AVEIyzC80rTbWEUatTW2lXpN8+8ILC8PiPeWPjwUzrPZDIOZ2wwqDmeqOYTdSGyL3+vE5GC3FB3Q==", "dev": true, "funding": [ { @@ -6700,9 +6700,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", - "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", "dev": true, "funding": [ { @@ -6720,7 +6720,7 @@ ], "dependencies": { "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001591", + "caniuse-lite": "^1.0.30001599", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -7616,9 +7616,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001594", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001594.tgz", - "integrity": "sha512-VblSX6nYqyJVs8DKFMldE2IVCJjZ225LW00ydtUWwh5hk9IfkTOffO6r8gJNsH0qqqeAF8KrbMYA2VEwTlGW5g==", + "version": "1.0.30001603", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001603.tgz", + "integrity": "sha512-iL2iSS0eDILMb9n5yKQoTBim9jMZ0Yrk8g0N9K7UzYyWnfIKzXBZD5ngpM37ZcL/cv0Mli8XtVMRYMQAfFpi5Q==", "dev": true, "funding": [ { @@ -8546,9 +8546,9 @@ } }, "node_modules/css-has-pseudo": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-6.0.2.tgz", - "integrity": "sha512-Z2Qm5yyOvJRTy6THdUlnGIX6PW/1wOc4FHWlfkcBkfkpZ3oz6lPdG+h+J7t1HZHT4uSSVR8XatXiMpqMUADXow==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-6.0.3.tgz", + "integrity": "sha512-qIsDxK/z0byH/mpNsv5hzQ5NOl8m1FRmOLgZpx4bG5uYHnOlO2XafeMI4mFIgNSViHwoUWcxSJZyyijaAmbs+A==", "dev": true, "funding": [ { @@ -8561,7 +8561,7 @@ } ], "dependencies": { - "@csstools/selector-specificity": "^3.0.2", + "@csstools/selector-specificity": "^3.0.3", "postcss-selector-parser": "^6.0.13", "postcss-value-parser": "^4.2.0" }, @@ -8695,9 +8695,9 @@ } }, "node_modules/cssdb": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.1.tgz", - "integrity": "sha512-F0nEoX/Rv8ENTHsjMPGHd9opdjGfXkgRBafSUGnQKPzGZFB7Lm0BbT10x21TMOCrKLbVsJ0NoCDMk6AfKqw8/A==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.0.0.tgz", + "integrity": "sha512-hfpm8VXc7/dhcEWpLvKDLwImOSk1sa2DxL36OEiY/4h2MGfKjPYIMZo4hnEEl+TCJr2GwcX46jF5TafRASDe9w==", "dev": true, "funding": [ { @@ -18118,9 +18118,9 @@ } }, "node_modules/postcss-color-functional-notation": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-6.0.7.tgz", - "integrity": "sha512-VwzaVfu1kEYDK2yM8ixeKA/QbgQ8k0uxpRevLH9Wam+R3C1sg68vnRB7m2AMhYfjqb5khp4p0EQk5aO90ASAkw==", + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-6.0.8.tgz", + "integrity": "sha512-BilFPTHcfWEnuQeqL83nbSPVK3tcU57S60aOrqgditarNDzOojyF0Gdc2Ur5L+zox366QjrCe0rOBLDO2pNvRQ==", "dev": true, "funding": [ { @@ -18133,10 +18133,10 @@ } ], "dependencies": { - "@csstools/css-color-parser": "^1.6.2", + "@csstools/css-color-parser": "^1.6.3", "@csstools/css-parser-algorithms": "^2.6.1", "@csstools/css-tokenizer": "^2.2.4", - "@csstools/postcss-progressive-custom-properties": "^3.1.1", + "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0" }, "engines": { @@ -18391,9 +18391,9 @@ } }, "node_modules/postcss-double-position-gradients": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-5.0.5.tgz", - "integrity": "sha512-26Tx4BfoxMNO9C89Nk56bfGv4jAwdDVgrQOyHZOP/6/D+xuOBf306KzTjHC2oBzaIIVtX+famOWHv4raxMjJMQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-5.0.6.tgz", + "integrity": "sha512-QJ+089FKMaqDxOhhIHsJrh4IP7h4PIHNC5jZP5PMmnfUScNu8Hji2lskqpFWCvu+5sj+2EJFyzKd13sLEWOZmQ==", "dev": true, "funding": [ { @@ -18406,7 +18406,7 @@ } ], "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^3.1.1", + "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0", "postcss-value-parser": "^4.2.0" }, @@ -18573,9 +18573,9 @@ } }, "node_modules/postcss-lab-function": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-6.0.12.tgz", - "integrity": "sha512-flHW2jdRCRe8ClhMgrylR1BCiyyqLLvp1qKfO5wuAclUihldfRsoDIFQWFVW7rJbruil9/LCoHNUvY9JwTlLPw==", + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-6.0.13.tgz", + "integrity": "sha512-tzEThi3prSyomnVqaAU+k/YJib4rxeeTKVfMt+mPcEugFgp0t6xRjoc7fzaWCoEwYLC6GxGLD8/Ugx8COCqabw==", "dev": true, "funding": [ { @@ -18588,10 +18588,10 @@ } ], "dependencies": { - "@csstools/css-color-parser": "^1.6.2", + "@csstools/css-color-parser": "^1.6.3", "@csstools/css-parser-algorithms": "^2.6.1", "@csstools/css-tokenizer": "^2.2.4", - "@csstools/postcss-progressive-custom-properties": "^3.1.1", + "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0" }, "engines": { @@ -18867,9 +18867,9 @@ } }, "node_modules/postcss-nesting": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.0.tgz", - "integrity": "sha512-QOYnosaZ+mlP6plQrAxFw09UUp2Sgtxj1BVHN+rSVbtV0Yx48zRt9/9F/ZOoxOKBBEsaJk2MYhhVRjeRRw5yuw==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.1.tgz", + "integrity": "sha512-qc74KvIAQNa5ujZKG1UV286dhaDW6basbUy2i9AzNU/T8C9hpvGu9NZzm1SfePe2yP7sPYgpA8d4sPVopn2Hhw==", "dev": true, "funding": [ { @@ -18883,7 +18883,7 @@ ], "dependencies": { "@csstools/selector-resolve-nested": "^1.1.0", - "@csstools/selector-specificity": "^3.0.2", + "@csstools/selector-specificity": "^3.0.3", "postcss-selector-parser": "^6.0.13" }, "engines": { @@ -19124,9 +19124,9 @@ } }, "node_modules/postcss-preset-env": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-9.5.2.tgz", - "integrity": "sha512-/KIAHELdg5BxsKA/Vc6Nok/66EM7lps8NulKcQWX2S52HdzxAqh+6HcuAFj7trRSW587vlOA4zCjlRFgR+W6Ag==", + "version": "9.5.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-9.5.3.tgz", + "integrity": "sha512-uOBG5kvYMxZGuepbAKr563PCB+syENPa1C9kPA8IvDGraVkrEUk//31oaO06oj9VtuujVtsgXHI7qbQynCuaVQ==", "dev": true, "funding": [ { @@ -19139,18 +19139,18 @@ } ], "dependencies": { - "@csstools/postcss-cascade-layers": "^4.0.3", - "@csstools/postcss-color-function": "^3.0.12", - "@csstools/postcss-color-mix-function": "^2.0.12", + "@csstools/postcss-cascade-layers": "^4.0.4", + "@csstools/postcss-color-function": "^3.0.13", + "@csstools/postcss-color-mix-function": "^2.0.13", "@csstools/postcss-exponential-functions": "^1.0.5", "@csstools/postcss-font-format-keywords": "^3.0.2", - "@csstools/postcss-gamut-mapping": "^1.0.5", - "@csstools/postcss-gradients-interpolation-method": "^4.0.13", - "@csstools/postcss-hwb-function": "^3.0.11", - "@csstools/postcss-ic-unit": "^3.0.5", + "@csstools/postcss-gamut-mapping": "^1.0.6", + "@csstools/postcss-gradients-interpolation-method": "^4.0.14", + "@csstools/postcss-hwb-function": "^3.0.12", + "@csstools/postcss-ic-unit": "^3.0.6", "@csstools/postcss-initial": "^1.0.1", - "@csstools/postcss-is-pseudo-class": "^4.0.5", - "@csstools/postcss-light-dark-function": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^4.0.6", + "@csstools/postcss-light-dark-function": "^1.0.2", "@csstools/postcss-logical-float-and-clear": "^2.0.1", "@csstools/postcss-logical-overflow": "^1.0.1", "@csstools/postcss-logical-overscroll-behavior": "^1.0.1", @@ -19160,38 +19160,38 @@ "@csstools/postcss-media-queries-aspect-ratio-number-values": "^2.0.7", "@csstools/postcss-nested-calc": "^3.0.2", "@csstools/postcss-normalize-display-values": "^3.0.2", - "@csstools/postcss-oklab-function": "^3.0.12", - "@csstools/postcss-progressive-custom-properties": "^3.1.1", - "@csstools/postcss-relative-color-syntax": "^2.0.12", + "@csstools/postcss-oklab-function": "^3.0.13", + "@csstools/postcss-progressive-custom-properties": "^3.2.0", + "@csstools/postcss-relative-color-syntax": "^2.0.13", "@csstools/postcss-scope-pseudo-class": "^3.0.1", "@csstools/postcss-stepped-value-functions": "^3.0.6", - "@csstools/postcss-text-decoration-shorthand": "^3.0.4", + "@csstools/postcss-text-decoration-shorthand": "^3.0.5", "@csstools/postcss-trigonometric-functions": "^3.0.6", "@csstools/postcss-unset-value": "^3.0.1", - "autoprefixer": "^10.4.18", + "autoprefixer": "^10.4.19", "browserslist": "^4.22.3", "css-blank-pseudo": "^6.0.1", - "css-has-pseudo": "^6.0.2", + "css-has-pseudo": "^6.0.3", "css-prefers-color-scheme": "^9.0.1", - "cssdb": "^7.11.1", + "cssdb": "^8.0.0", "postcss-attribute-case-insensitive": "^6.0.3", "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^6.0.7", + "postcss-color-functional-notation": "^6.0.8", "postcss-color-hex-alpha": "^9.0.4", "postcss-color-rebeccapurple": "^9.0.3", "postcss-custom-media": "^10.0.4", "postcss-custom-properties": "^13.3.6", "postcss-custom-selectors": "^7.1.8", "postcss-dir-pseudo-class": "^8.0.1", - "postcss-double-position-gradients": "^5.0.5", + "postcss-double-position-gradients": "^5.0.6", "postcss-focus-visible": "^9.0.1", "postcss-focus-within": "^8.0.1", "postcss-font-variant": "^5.0.0", "postcss-gap-properties": "^5.0.1", "postcss-image-set-function": "^6.0.3", - "postcss-lab-function": "^6.0.12", + "postcss-lab-function": "^6.0.13", "postcss-logical": "^7.0.1", - "postcss-nesting": "^12.1.0", + "postcss-nesting": "^12.1.1", "postcss-opacity-percentage": "^2.0.0", "postcss-overflow-shorthand": "^5.0.1", "postcss-page-break": "^3.0.4", diff --git a/search/includes/classes/class-settingshealthjob.php b/search/includes/classes/class-settingshealthjob.php index a998845313..1df00750c5 100644 --- a/search/includes/classes/class-settingshealthjob.php +++ b/search/includes/classes/class-settingshealthjob.php @@ -4,7 +4,8 @@ use Automattic\VIP\Search\Health; use Automattic\VIP\Utils\Alerts; -use WP_CLI; +use ElasticPress\Indexable; +use WP_Error; require_once __DIR__ . '/class-health.php'; @@ -209,10 +210,11 @@ public function heal_index_settings( $unhealthy_indexables ) { continue; } + /** @var Indexable|bool|WP_Error */ $indexable = $this->indexables->get( $indexable_slug ); if ( is_wp_error( $indexable ) || ! $indexable ) { - $error_message = is_wp_error( $indexable ) ? $indexable->get_error_message() : 'Indexable not found'; + $error_message = is_wp_error( $indexable ) ? /** @var WP_Error $indexable */ $indexable->get_error_message() : 'Indexable not found'; $message = sprintf( 'Application %s: Failed to load indexable %s when healing index settings on %s: %s', FILES_CLIENT_SITE_ID, @@ -249,6 +251,7 @@ public function heal_index_settings( $unhealthy_indexables ) { $result = $this->health->heal_index_settings_for_indexable( $indexable, $options ); if ( is_wp_error( $result['result'] ) ) { + /** @var WP_Error $result */ $message = sprintf( 'Application %s: Failed to heal index settings for indexable %s and index version %d on %s: %s', FILES_CLIENT_SITE_ID, @@ -360,8 +363,6 @@ public function maybe_process_build( $indexable ) { home_url() ); $this->send_alert( '#vip-go-es-alerts', $message, 2 ); - - return; } elseif ( ! wp_next_scheduled( self::CRON_EVENT_BUILD_NAME, [ $indexable->slug ] ) ) { wp_schedule_single_event( time() + 30, self::CRON_EVENT_BUILD_NAME, [ $indexable->slug ] ); } @@ -381,7 +382,7 @@ public function build_new_index( $indexable_slug, $last_processed_id = false ) { $indexable = $this->indexables->get( $indexable_slug ); if ( ! $indexable ) { - $indexable = new \WP_Error( 'indexable-not-found', sprintf( 'Indexable %s not found - is the feature active?', $indexable_slug ) ); + $indexable = new WP_Error( 'indexable-not-found', sprintf( 'Indexable %s not found - is the feature active?', $indexable_slug ) ); } if ( is_wp_error( $indexable ) ) { $message = sprintf( diff --git a/search/includes/classes/queue/class-cron.php b/search/includes/classes/queue/class-cron.php index 844b0aafc7..bac2efd1f0 100644 --- a/search/includes/classes/queue/class-cron.php +++ b/search/includes/classes/queue/class-cron.php @@ -93,7 +93,7 @@ public function get_max_concurrent_processor_job_count() { return 1; } - $allowed_total_concurrency = (int) ceil( \Automattic\WP\Cron_Control\JOB_CONCURRENCY_LIMIT / 4 ); + $allowed_total_concurrency = (int) ceil( constant( 'Automattic\\WP\\Cron_Control\\JOB_CONCURRENCY_LIMIT' ) / 4 ); return min( self::MAX_PROCESSOR_JOB_COUNT, $allowed_total_concurrency ); } diff --git a/tests/admin-notice/conditions/test-class-capability-condition.php b/tests/admin-notice/conditions/test-class-capability-condition.php index 6e3d122de2..d5388f2eec 100644 --- a/tests/admin-notice/conditions/test-class-capability-condition.php +++ b/tests/admin-notice/conditions/test-class-capability-condition.php @@ -13,15 +13,11 @@ class Capability_Condition_Test extends TestCase { public function setUp(): void { self::$mock_global_functions = $this->getMockBuilder( self::class ) - ->setMethods( [ 'mock_current_user_can' ] ) + ->addMethods( [ 'mock_current_user_can' ] ) ->getMock(); } - public function mock_current_user_can( string $capability, ...$args ) { - } - public function evaluate_data() { - return [ [ [ true ], true ], [ [ true, true ], true ], @@ -36,7 +32,6 @@ public function evaluate_data() { * @dataProvider evaluate_data */ public function test__evaluate( $has_capabilities, $expected_result ) { - self::$mock_global_functions->method( 'mock_current_user_can' ) ->will( $this->onConsecutiveCalls( ...$has_capabilities ) ); @@ -46,7 +41,6 @@ public function test__evaluate( $has_capabilities, $expected_result ) { } } - /** * Overwriting global function so that no real remote request is called */ diff --git a/tests/admin-notice/test-class-admin-notice-controller.php b/tests/admin-notice/test-class-admin-notice-controller.php index 3ccee4307a..93e7b3c894 100644 --- a/tests/admin-notice/test-class-admin-notice-controller.php +++ b/tests/admin-notice/test-class-admin-notice-controller.php @@ -2,6 +2,7 @@ namespace Automattic\VIP\Admin_Notice; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; require_once __DIR__ . '/../../admin-notice/class-admin-notice-controller.php'; @@ -13,19 +14,11 @@ class Admin_Notice_Controller_Test extends TestCase { public function setUp(): void { self::$mock_global_functions = $this->getMockBuilder( self::class ) - ->setMethods( [ 'add_user_meta', 'get_user_meta', 'delete_user_meta' ] ) + ->addMethods( [ 'add_user_meta', 'get_user_meta', 'delete_user_meta' ] ) ->getMock(); } - public function add_user_meta( int $user_id, string $meta_key, $meta_value, bool $unique = false ) { - } - public function get_user_meta( int $user_id, string $key = '', bool $single = false ) { - } - public function delete_user_meta( int $user_id, string $meta_key, $meta_value ) { - } - public function mayby_clean_stale_dismissed_notices_data() { - return [ [ 101, true ], [ -1, false ], @@ -38,8 +31,9 @@ public function mayby_clean_stale_dismissed_notices_data() { public function test__mayby_clean_stale_dismissed_notices( $limit_value, $expect_called ) { Admin_Notice_Controller::$stale_dismiss_cleanup_value = $limit_value; + /** @var MockObject&Admin_Notice_Controller */ $partially_mocked_controller = $this->getMockBuilder( Admin_Notice_Controller::class ) - ->setMethods( [ 'clean_stale_dismissed_notices' ] ) + ->onlyMethods( [ 'clean_stale_dismissed_notices' ] ) ->getMock(); $partially_mocked_controller->expects( $expect_called ? $this->once() : $this->never() ) @@ -49,7 +43,6 @@ public function test__mayby_clean_stale_dismissed_notices( $limit_value, $expect } public function clean_stale_dismissed_notices_data() { - return [ [ [], [ 'a' ], [] ], [ [ 'a' ], [ 'a' ], [] ], @@ -84,9 +77,11 @@ public function test__clean_stale_dismissed_notices( $dismissed_notices, $regist function add_user_meta( int $user_id, string $meta_key, $meta_value, bool $unique = false ) { return is_null( Admin_Notice_Controller_Test::$mock_global_functions ) ? null : Admin_Notice_Controller_Test::$mock_global_functions->add_user_meta( $user_id, $meta_key, $meta_value, $unique ); } + function get_user_meta( int $user_id, string $key = '', bool $single = false ) { return is_null( Admin_Notice_Controller_Test::$mock_global_functions ) ? null : Admin_Notice_Controller_Test::$mock_global_functions->get_user_meta( $user_id, $key, $single ); } + function delete_user_meta( int $user_id, string $meta_key, $meta_value ) { return is_null( Admin_Notice_Controller_Test::$mock_global_functions ) ? null : Admin_Notice_Controller_Test::$mock_global_functions->delete_user_meta( $user_id, $meta_key, $meta_value ); } diff --git a/tests/files/test-vip-filesystem.php b/tests/files/test-vip-filesystem.php index c23692bd69..7d0820860f 100644 --- a/tests/files/test-vip-filesystem.php +++ b/tests/files/test-vip-filesystem.php @@ -4,6 +4,7 @@ use Automattic\Test\Constant_Mocker; use ErrorException; +use PHPUnit\Framework\MockObject\MockObject; use WP_Error; use WP_Filesystem_Base; use WP_Filesystem_Direct; @@ -29,8 +30,8 @@ class VIP_Filesystem_Test extends WP_UnitTestCase { public static function configure_constant_mocker(): void { Constant_Mocker::clear(); - define( 'LOCAL_UPLOADS', '/tmp/uploads' ); - define( 'WP_CONTENT_DIR', '/tmp/wordpress/wp-content' ); + define( 'LOCAL_UPLOADS', '/wp/uploads' ); + define( 'WP_CONTENT_DIR', '/wp/wordpress/wp-content' ); } public function setUp(): void { @@ -50,7 +51,7 @@ public function setUp(): void { set_error_handler( static function ( int $errno, string $errstr ) { if ( $errno & error_reporting() ) { // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- CLI - throw new ErrorException( $errstr, $errno ); + throw new ErrorException( $errstr, $errno ); // NOSONAR } return false; @@ -265,14 +266,15 @@ public function test__filter_validate_file__valid_file() { ]; $basepath = $this->get_upload_path(); + /** @var MockObject&VIP_Filesystem */ $stub = $this->getMockBuilder( VIP_Filesystem::class ) - ->setMethods( [ 'validate_file_name' ] ) - ->getMock(); + ->onlyMethods( [ 'validate_file_name' ] ) + ->getMock(); $stub->expects( $this->once() ) - ->method( 'validate_file_name' ) - ->with( $basepath . '/' . $file['name'] ) - ->will( $this->returnValue( $file['name'] ) ); + ->method( 'validate_file_name' ) + ->with( $basepath . '/' . $file['name'] ) + ->will( $this->returnValue( $file['name'] ) ); $actual = $stub->filter_validate_file( $file ); @@ -287,8 +289,9 @@ public function test__filter_validate_file__unique_file() { $unique_file_name = 'testfile_8hj30h.txt'; $basepath = $this->get_upload_path(); + /** @var MockObject&VIP_Filesystem */ $stub = $this->getMockBuilder( VIP_Filesystem::class ) - ->setMethods( [ 'validate_file_name' ] ) + ->onlyMethods( [ 'validate_file_name' ] ) ->getMock(); $stub->expects( $this->once() ) @@ -308,14 +311,15 @@ public function test__filter_validate_file__invalid_file_length() { ]; $basepath = $this->get_upload_path(); + /** @var MockObject&VIP_Filesystem */ $stub = $this->getMockBuilder( VIP_Filesystem::class ) - ->setMethods( [ 'validate_file_name' ] ) - ->getMock(); + ->onlyMethods( [ 'validate_file_name' ] ) + ->getMock(); $stub->expects( $this->once() ) - ->method( 'validate_file_name' ) - ->with( $basepath . '/' . $file['name'] ) - ->will( $this->returnValue( $file['name'] ) ); + ->method( 'validate_file_name' ) + ->with( $basepath . '/' . $file['name'] ) + ->will( $this->returnValue( $file['name'] ) ); $actual = $stub->filter_validate_file( $file ); @@ -332,14 +336,15 @@ public function test__filter_validate_file__invalid_file_type() { ]; $basepath = $this->get_upload_path(); + /** @var MockObject&VIP_Filesystem */ $stub = $this->getMockBuilder( VIP_Filesystem::class ) - ->setMethods( [ 'validate_file_name' ] ) - ->getMock(); + ->onlyMethods( [ 'validate_file_name' ] ) + ->getMock(); $stub->expects( $this->once() ) - ->method( 'validate_file_name' ) - ->with( $basepath . '/' . $file['name'] ) - ->will( $this->returnValue( new WP_Error( 'invalid-file-type', 'Failed to generate new unique file name `testfile.exe` (response code: 400)' ) ) ); + ->method( 'validate_file_name' ) + ->with( $basepath . '/' . $file['name'] ) + ->will( $this->returnValue( new WP_Error( 'invalid-file-type', 'Failed to generate new unique file name `testfile.exe` (response code: 400)' ) ) ); $actual = $stub->filter_validate_file( $file ); @@ -386,23 +391,4 @@ public function data_get_transport_for_path(): iterable { [ constant( 'WP_CONTENT_DIR' ) . '/languages/test.txt', 'direct' ], ]; } - - public function test_wp_font_dir() { - // Only available in WP 6.5 and newer: - if ( ! function_exists( '\wp_get_font_dir' ) ) { - $this->markTestSkipped( 'test_wp_font_dir does not need to run for WP < 6.5.' ); - return; - } - - $font_dir = \wp_get_font_dir(); - - $this->assertEquals( $font_dir, [ - 'path' => 'vip://wp-content/uploads/fonts', - 'basedir' => 'vip://wp-content/uploads/fonts', - 'url' => 'http://example.org/wp-content/uploads/fonts', - 'baseurl' => 'http://example.org/wp-content/uploads/fonts', - 'subdir' => '', - 'error' => false, - ] ); - } } diff --git a/tests/files/test-wp-filesystem-vip.php b/tests/files/test-wp-filesystem-vip.php index a7e10c38c8..ad572dfa1d 100644 --- a/tests/files/test-wp-filesystem-vip.php +++ b/tests/files/test-wp-filesystem-vip.php @@ -372,7 +372,16 @@ public function test_move_with_no_filesystem(): void { $source = $tmp . 'source.txt'; $dest = $tmp . 'dest.txt'; - $actual = $wp_filesystem->move( $source, $dest ); + // See https://github.com/Automattic/vip-go-mu-plugins/issues/5445 + // WP 6.1.4 does not check whether the file exists and spits a warning. + $original = error_reporting(); + error_reporting( $original & ~E_WARNING ); + try { + $actual = $wp_filesystem->move( $source, $dest ); + } finally { + error_reporting( $original ); + } + self::assertFalse( $actual ); } finally { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited diff --git a/tests/integrations/test-integration-vip-config.php b/tests/integrations/test-integration-vip-config.php index 54ea272ad0..90fd1d5369 100644 --- a/tests/integrations/test-integration-vip-config.php +++ b/tests/integrations/test-integration-vip-config.php @@ -8,9 +8,13 @@ namespace Automattic\VIP\Integrations; // phpcs:disable Squiz.Commenting.ClassComment.Missing, Squiz.Commenting.FunctionComment.Missing, Squiz.Commenting.FunctionComment.MissingParamComment +// phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting +// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler use Org_Integration_Status; use Env_Integration_Status; +use ErrorException; +use PHPUnit\Framework\MockObject\MockObject; use WP_UnitTestCase; use function Automattic\Test\Utils\get_class_method_as_public; @@ -19,6 +23,28 @@ require_once __DIR__ . '/fake-integration.php'; class VIP_Integration_Vip_Config_Test extends WP_UnitTestCase { + private $original_error_reporting; + + public function setUp(): void { + parent::setUp(); + + $this->original_error_reporting = error_reporting(); + set_error_handler( static function ( int $errno, string $errstr ) { + if ( error_reporting() & $errno ) { + // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- CLI + throw new ErrorException( $errstr, $errno ); // NOSONAR + } + + return false; + }, E_USER_WARNING ); + } + + public function tearDown(): void { + restore_error_handler(); + error_reporting( $this->original_error_reporting ); + parent::tearDown(); + } + public function test__get_vip_config_from_file_returns_null_if_config_file_does_not_exist(): void { $slug = 'dummy'; $integration_config = new IntegrationVipConfig( $slug ); @@ -223,7 +249,8 @@ private function do_test_get_site_config( } public function test__get_value_from_vip_config_trigger_error_if_invalid_argument_is_passed(): void { - $this->expectException( 'PHPUnit_Framework_Error_Warning' ); + $this->expectException( ErrorException::class ); + $this->expectExceptionCode( E_USER_WARNING ); $this->expectExceptionMessage( 'config_type param (invalid) must be one of org, env or network_sites.' ); $mocked_vip_configs = []; @@ -313,20 +340,18 @@ private function do_test_get_value_from_config( * * @param array|null|string $vip_config * - * @return MockObject + * @return MockObject&IntegrationVipConfig */ private function get_mock( $vip_config ) { /** * Config Mock. * - * @var MockObject + * @var MockObject&IntegrationVipConfig */ $mock = $this->getMockBuilder( IntegrationVipConfig::class ) - ->disableOriginalConstructor() - ->setMethods( [ - 'get_vip_config_from_file', - ] ) - ->getMock(); + ->disableOriginalConstructor() + ->onlyMethods( [ 'get_vip_config_from_file' ] ) + ->getMock(); $mock->method( 'get_vip_config_from_file' )->willReturn( $vip_config ); $mock->__construct( 'slug' ); diff --git a/tests/integrations/test-integration.php b/tests/integrations/test-integration.php index f5ac922355..894cc6ea21 100644 --- a/tests/integrations/test-integration.php +++ b/tests/integrations/test-integration.php @@ -8,15 +8,40 @@ namespace Automattic\VIP\Integrations; // phpcs:disable Squiz.Commenting.ClassComment.Missing, Squiz.Commenting.FunctionComment.Missing, Squiz.Commenting.FunctionComment.MissingParamComment +// phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting +// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler use Automattic\VIP\Integrations\IntegrationVipConfig; use Env_Integration_Status; +use ErrorException; use PHPUnit\Framework\MockObject\MockObject; use WP_UnitTestCase; require_once __DIR__ . '/fake-integration.php'; class VIP_Integration_Test extends WP_UnitTestCase { + private $original_error_reporting; + + public function setUp(): void { + parent::setUp(); + + $this->original_error_reporting = error_reporting(); + set_error_handler( static function ( int $errno, string $errstr ) { + if ( error_reporting() & $errno ) { + // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- CLI + throw new ErrorException( $errstr, $errno ); // NOSONAR + } + + return false; + }, E_USER_WARNING ); + } + + public function tearDown(): void { + restore_error_handler(); + error_reporting( $this->original_error_reporting ); + parent::tearDown(); + } + public function test__slug_is_setting_up_on_instantiation(): void { $integration = new FakeIntegration( 'fake' ); @@ -46,14 +71,15 @@ public function test__activate_is_setting_up_the_plugins_config(): void { } public function test__calling_activate_when_the_integration_is_already_loaded_does_not_activate_the_integration_again(): void { - $this->expectException( 'PHPUnit_Framework_Error_Warning' ); + $this->expectException( ErrorException::class ); + $this->expectExceptionCode( E_USER_WARNING ); $this->expectExceptionMessage( 'Prevented activating of integration with slug "fake" because it is already loaded.' ); /** * Integration mock. * * @var MockObject|FakeIntegration */ - $integration_mock = $this->getMockBuilder( FakeIntegration::class )->setConstructorArgs( [ 'fake' ] )->setMethods( [ 'is_loaded' ] )->getMock(); + $integration_mock = $this->getMockBuilder( FakeIntegration::class )->setConstructorArgs( [ 'fake' ] )->onlyMethods( [ 'is_loaded' ] )->getMock(); $integration_mock->expects( $this->once() )->method( 'is_loaded' )->willReturn( true ); $integration_mock->activate(); @@ -62,7 +88,8 @@ public function test__calling_activate_when_the_integration_is_already_loaded_do } public function test__calling_activate_twice_on_same_integration_does_not_activate_the_plugin_second_time(): void { - $this->expectException( 'PHPUnit_Framework_Error_Warning' ); + $this->expectException( ErrorException::class ); + $this->expectExceptionCode( E_USER_WARNING ); $this->expectExceptionMessage( 'VIP Integration with slug "fake" is already activated.' ); $integration = new FakeIntegration( 'fake' ); @@ -86,7 +113,7 @@ public function test__switch_blog_callback_is_setting_the_correct_config_for_net * * @var IntegrationVipConfig|MockObject */ - $config_mock = $this->getMockBuilder( IntegrationVipConfig::class )->disableOriginalConstructor()->setMethods( [ 'get_vip_config_from_file' ] )->getMock(); + $config_mock = $this->getMockBuilder( IntegrationVipConfig::class )->disableOriginalConstructor()->onlyMethods( [ 'get_vip_config_from_file' ] )->getMock(); $config_mock->method( 'get_vip_config_from_file' )->willReturn( [ 'network_sites' => [ get_current_blog_id() => [ @@ -104,7 +131,7 @@ public function test__switch_blog_callback_is_setting_the_correct_config_for_net // By default return config passed via activate(). $this->assertEquals( array( 'activate_config' ), $integration->get_config() ); - + // If blog is switched then return config of current network site. switch_to_blog( $blog_2_id ); $this->assertEquals( array( 'network_site_2_config' ), $integration->get_config() ); @@ -121,7 +148,8 @@ public function test__is_active_returns_false_when_integration_is_not_active(): } public function test__set_vip_config_function_throws_error_if_integration_is_not_active(): void { - $this->expectException( 'PHPUnit_Framework_Error_Warning' ); + $this->expectException( ErrorException::class ); + $this->expectExceptionCode( E_USER_WARNING ); $this->expectExceptionMessage( 'Configuration info can only assigned if integration is active.' ); $integration = new FakeIntegration( 'fake' ); diff --git a/tests/integrations/test-integrations.php b/tests/integrations/test-integrations.php index 21fc3563f9..4f8c73e639 100644 --- a/tests/integrations/test-integrations.php +++ b/tests/integrations/test-integrations.php @@ -8,7 +8,10 @@ namespace Automattic\VIP\Integrations; // phpcs:disable Squiz.Commenting.ClassComment.Missing, Squiz.Commenting.FunctionComment.Missing, Squiz.Commenting.FunctionComment.MissingParamComment +// phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting +// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler +use ErrorException; use WP_UnitTestCase; use PHPUnit\Framework\MockObject\MockObject; use stdClass; @@ -18,8 +21,30 @@ require_once __DIR__ . '/fake-integration.php'; class VIP_Integrations_Test extends WP_UnitTestCase { + private $original_error_reporting; + + public function setUp(): void { + parent::setUp(); + + $this->original_error_reporting = error_reporting(); + set_error_handler( static function ( int $errno, string $errstr ) { + if ( error_reporting() & $errno ) { + // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- CLI + throw new ErrorException( $errstr, $errno ); // NOSONAR + } + + return false; + }, E_USER_WARNING ); + } + + public function tearDown(): void { + restore_error_handler(); + error_reporting( $this->original_error_reporting ); + parent::tearDown(); + } + public function test__integrations_are_activating_based_on_given_vip_config(): void { - $config_mock = $this->getMockBuilder( IntegrationVipConfig::class )->disableOriginalConstructor()->setMethods( [ 'is_active_via_vip', 'get_site_config' ] )->getMock(); + $config_mock = $this->getMockBuilder( IntegrationVipConfig::class )->disableOriginalConstructor()->onlyMethods( [ 'is_active_via_vip', 'get_site_config' ] )->getMock(); $config_mock->expects( $this->exactly( 2 ) )->method( 'is_active_via_vip' )->willReturnOnConsecutiveCalls( true, false ); $config_mock->expects( $this->exactly( 1 ) )->method( 'get_site_config' )->willReturnOnConsecutiveCalls( [ 'config_key_1' => 'vip_value' ] ); @@ -28,7 +53,7 @@ public function test__integrations_are_activating_based_on_given_vip_config(): v * * @var MockObject|Integrations */ - $mock = $this->getMockBuilder( Integrations::class )->setMethods( [ 'get_integration_vip_config' ] )->getMock(); + $mock = $this->getMockBuilder( Integrations::class )->onlyMethods( [ 'get_integration_vip_config' ] )->getMock(); $mock->expects( $this->any() )->method( 'get_integration_vip_config' )->willReturn( $config_mock ); $integration_1 = new FakeIntegration( 'fake-1' ); @@ -53,21 +78,21 @@ public function test__integrations_are_activating_based_on_given_vip_config(): v } public function test__expected_methods_are_getting_called_when_the_integration_is_activated_via_vip_config(): void { - $config_mock = $this->getMockBuilder( IntegrationVipConfig::class )->disableOriginalConstructor()->setMethods( [ 'is_active_via_vip' ] )->getMock(); + $config_mock = $this->getMockBuilder( IntegrationVipConfig::class )->disableOriginalConstructor()->onlyMethods( [ 'is_active_via_vip' ] )->getMock(); $config_mock->expects( $this->once() )->method( 'is_active_via_vip' )->willReturn( true ); /** * Integrations mock. * * @var MockObject|Integrations */ - $integrations_mock = $this->getMockBuilder( Integrations::class )->setMethods( [ 'get_integration_vip_config' ] )->getMock(); + $integrations_mock = $this->getMockBuilder( Integrations::class )->onlyMethods( [ 'get_integration_vip_config' ] )->getMock(); $integrations_mock->expects( $this->once() )->method( 'get_integration_vip_config' )->willReturn( $config_mock ); /** * Integration mock. * * @var MockObject|FakeIntegration */ - $integration_mock = $this->getMockBuilder( FakeIntegration::class )->setConstructorArgs( [ 'fake' ] )->setMethods( [ 'configure', 'set_vip_config' ] )->getMock(); + $integration_mock = $this->getMockBuilder( FakeIntegration::class )->setConstructorArgs( [ 'fake' ] )->onlyMethods( [ 'configure', 'set_vip_config' ] )->getMock(); $integration_mock->expects( $this->once() )->method( 'configure' ); $integration_mock->expects( $this->once() )->method( 'set_vip_config' ); @@ -108,7 +133,8 @@ public function test__load_active_does_not_loads_the_non_activated_integration() } public function test__double_slug_registration_throws_invalidArgumentException(): void { - $this->expectException( 'PHPUnit_Framework_Error_Warning' ); + $this->expectException( ErrorException::class ); + $this->expectExceptionCode( E_USER_WARNING ); $this->expectExceptionMessage( 'Integration with slug "fake" is already registered.' ); $integrations = new Integrations(); @@ -119,7 +145,8 @@ public function test__double_slug_registration_throws_invalidArgumentException() } public function test__non_integration_subclass_throws_invalidArgumentException(): void { - $this->expectException( 'PHPUnit_Framework_Error_Warning' ); + $this->expectException( ErrorException::class ); + $this->expectExceptionCode( E_USER_WARNING ); $this->expectExceptionMessage( 'Integration class "stdClass" must extend Automattic\VIP\Integrations\Integration.' ); $integrations = new Integrations(); @@ -129,7 +156,8 @@ public function test__non_integration_subclass_throws_invalidArgumentException() } public function test__activating_integration_by_passing_invalid_slug_throws_invalidArgumentException(): void { - $this->expectException( 'PHPUnit_Framework_Error_Warning' ); + $this->expectException( ErrorException::class ); + $this->expectExceptionCode( E_USER_WARNING ); $this->expectExceptionMessage( 'VIP Integration with slug "invalid-slug" is not a registered integration.' ); $integrations = new Integrations(); diff --git a/tests/integrations/test-parsely.php b/tests/integrations/test-parsely.php index 65fced2d35..e0642d0975 100644 --- a/tests/integrations/test-parsely.php +++ b/tests/integrations/test-parsely.php @@ -30,7 +30,7 @@ public function test__load_call_returns_without_setting_constant_if_parsely_is_a * * @var MockObject|ParselyIntegration */ - $parsely_integration_mock = $this->getMockBuilder( ParselyIntegration::class )->setConstructorArgs( [ 'parsely' ] )->setMethods( [ 'is_loaded' ] )->getMock(); + $parsely_integration_mock = $this->getMockBuilder( ParselyIntegration::class )->setConstructorArgs( [ 'parsely' ] )->onlyMethods( [ 'is_loaded' ] )->getMock(); $parsely_integration_mock->expects( $this->once() )->method( 'is_loaded' )->willReturn( true ); $preload_state = defined( 'VIP_PARSELY_ENABLED' ); @@ -45,13 +45,13 @@ public function test__load_call_is_setting_the_enabled_constant_if_no_constant_i * * @var MockObject|ParselyIntegration */ - $parsely_integration_mock = $this->getMockBuilder( ParselyIntegration::class )->setConstructorArgs( [ 'parsely' ] )->setMethods( [ 'is_loaded' ] )->getMock(); + $parsely_integration_mock = $this->getMockBuilder( ParselyIntegration::class )->setConstructorArgs( [ 'parsely' ] )->onlyMethods( [ 'is_loaded' ] )->getMock(); $parsely_integration_mock->expects( $this->once() )->method( 'is_loaded' )->willReturn( false ); $existing_value = defined( 'VIP_PARSELY_ENABLED' ) ? VIP_PARSELY_ENABLED : null; $parsely_integration_mock->load(); - if ( is_null( $existing_value ) || true == $existing_value ) { + if ( is_null( $existing_value ) || $existing_value ) { $this->assertTrue( VIP_PARSELY_ENABLED ); } else { $this->assertFalse( defined( 'VIP_PARSELY_ENABLED' ) ); diff --git a/tests/mock-constants.php b/tests/mock-constants.php index 88a169aefc..6f7a4ac415 100644 --- a/tests/mock-constants.php +++ b/tests/mock-constants.php @@ -20,6 +20,7 @@ public static function clear(): void { } public static function define( string $constant, $value ): void { + $constant = ltrim( $constant, '\\' ); if ( isset( self::$constants[ $constant ] ) ) { // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- CLI throw new InvalidArgumentException( sprintf( "Constant \"%s\" is already defined. Stacktrace:\n%s", $constant, self::$constants[ $constant ][1] ) ); @@ -31,10 +32,12 @@ public static function define( string $constant, $value ): void { } public static function defined( string $constant ): bool { + $constant = ltrim( $constant, '\\' ); return isset( self::$constants[ $constant ] ); } public static function constant( string $constant ) { + $constant = ltrim( $constant, '\\' ); if ( ! isset( self::$constants[ $constant ] ) ) { // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- CLI throw new InvalidArgumentException( sprintf( 'Constant "%s" is not defined', $constant ) ); @@ -44,6 +47,7 @@ public static function constant( string $constant ) { } public static function undefine( string $constant ): void { + $constant = ltrim( $constant, '\\' ); unset( self::$constants[ $constant ] ); } } diff --git a/tests/performance/test-class-mime-types-caching.php b/tests/performance/test-class-mime-types-caching.php index d8a1a7ff20..b89bee71a2 100644 --- a/tests/performance/test-class-mime-types-caching.php +++ b/tests/performance/test-class-mime-types-caching.php @@ -219,13 +219,8 @@ public function test__count_query_failure() { // phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited -- Mock $wpdb. $mock_builder = $this->getMockBuilder( \wpdb::class ) - ->setConstructorArgs( array( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST ) ); - - if ( method_exists( $mock_builder, 'onlyMethods' ) ) { - $mock_builder = $mock_builder->onlyMethods( array( 'get_var' ) ); - } else { - $mock_builder = $mock_builder->setMethods( array( 'get_var' ) ); - } + ->setConstructorArgs( array( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST ) ) + ->onlyMethods( array( 'get_var' ) ); $wpdb = $mock_builder->getMock(); $wpdb->method( 'get_var' )->willReturn( null ); diff --git a/tests/search/includes/classes/queue/test-class-cron.php b/tests/search/includes/classes/queue/test-class-cron.php index 100b78e9f1..d31146926a 100644 --- a/tests/search/includes/classes/queue/test-class-cron.php +++ b/tests/search/includes/classes/queue/test-class-cron.php @@ -98,10 +98,9 @@ public function test_disable_sweeper_job() { public function test_process_jobs() { $mock_queue = $this->getMockBuilder( Queue::class ) - ->setMethods( [ 'get_jobs_by_range', 'process_jobs' ] ) + ->onlyMethods( [ 'get_jobs_by_range', 'process_jobs' ] ) ->getMock(); - $options = [ 'min_id' => 1, 'max_id' => 2, @@ -147,10 +146,11 @@ public function test_schedule_batch_job() { /** @var Cron&MockObject */ $partially_mocked_cron = $this->getMockBuilder( Cron::class ) - ->setMethods( [ 'get_processor_job_count', 'get_max_concurrent_processor_job_count' ] ) + ->onlyMethods( [ 'get_processor_job_count', 'get_max_concurrent_processor_job_count' ] ) ->getMock(); - $mock_queue = $this->getMockBuilder( Queue::class ) - ->setMethods( [ 'checkout_jobs', 'free_deadlocked_jobs' ] ) + + $mock_queue = $this->getMockBuilder( Queue::class ) + ->onlyMethods( [ 'checkout_jobs', 'free_deadlocked_jobs' ] ) ->getMock(); $mock_jobs = array( @@ -229,10 +229,9 @@ public function schedule_batch_job__scheduling_limits_data() { public function test_schedule_batch_job__scheduling_limits( $job_counts, $expected_shedule_count ) { /** @var Cron&MockObject */ $partially_mocked_cron = $this->getMockBuilder( Cron::class ) - ->setMethods( [ 'get_processor_job_count', 'schedule_batch_job', 'get_max_concurrent_processor_job_count' ] ) + ->onlyMethods( [ 'get_processor_job_count', 'schedule_batch_job', 'get_max_concurrent_processor_job_count' ] ) ->getMock(); - $partially_mocked_cron->queue = $this->createMock( \Automattic\VIP\Search\Queue::class ); $partially_mocked_cron->expects( $this->exactly( $expected_shedule_count ) ) @@ -282,11 +281,9 @@ public function configure_concurrency_data() { /** * @dataProvider configure_concurrency_data - * @runInSeparateProcess - * @preserveGlobalState disabled */ public function test_configure_concurrency( $cron_limit, $expected ) { - \define( 'Automattic\WP\Cron_Control\JOB_CONCURRENCY_LIMIT', $cron_limit ); + define( 'Automattic\WP\Cron_Control\JOB_CONCURRENCY_LIMIT', $cron_limit ); $result = $this->cron->configure_concurrency( [] ); diff --git a/tests/search/includes/classes/test-class-health.php b/tests/search/includes/classes/test-class-health.php index 1e25ce9460..549911eb52 100644 --- a/tests/search/includes/classes/test-class-health.php +++ b/tests/search/includes/classes/test-class-health.php @@ -5,6 +5,10 @@ use PHPUnit\Framework\MockObject\MockObject; use WP_UnitTestCase; use Automattic\Test\Constant_Mocker; +use ElasticPress\Elasticsearch; +use ElasticPress\Indexable; +use ElasticPress\Indexables; +use WP_Error; require_once __DIR__ . '/../../../../search/search.php'; require_once __DIR__ . '/../../../../search/includes/classes/class-health.php'; @@ -20,16 +24,19 @@ class Health_Test extends WP_UnitTestCase { private static $indexable_methods = [ 'query_es', 'query_db', - 'format_args', 'get_mapping', 'prepare_document', 'put_mapping', - 'build_mapping', 'index_exists', 'get_index_name', + 'generate_mapping', + ]; + + private static $indexable_children_methods = [ + 'format_args', + 'build_mapping', 'get_index_settings', 'update_index_settings', - 'generate_mapping', ]; /** @var Search */ @@ -49,7 +56,7 @@ public function test_get_missing_docs_or_posts_diff() { $found_post_ids = array( 1, 3, 5 ); $found_document_ids = array( 1, 3, 7 ); - $diff = \Automattic\VIP\Search\Health::get_missing_docs_or_posts_diff( $found_post_ids, $found_document_ids ); + $diff = Health::get_missing_docs_or_posts_diff( $found_post_ids, $found_document_ids ); $expected_diff = array( 'post_5' => array( @@ -116,7 +123,7 @@ public function test_filter_expected_post_rows() { $indexed_post_types = array( 'post' ); $indexed_post_statuses = array( 'publish' ); - $filtered = \Automattic\VIP\Search\Health::filter_expected_post_rows( $rows, $indexed_post_types, $indexed_post_statuses ); + $filtered = Health::filter_expected_post_rows( $rows, $indexed_post_types, $indexed_post_statuses ); // Grab just the IDs to make validation simpler $filtered_ids = array_values( wp_list_pluck( $filtered, 'ID' ) ); @@ -256,7 +263,7 @@ public function get_diff_document_and_prepared_document_data() { * @dataProvider get_diff_document_and_prepared_document_data */ public function test_diff_document_and_prepared_document( $prepared_document, $document, $expected_diff ) { - $diff = \Automattic\VIP\Search\Health::diff_document_and_prepared_document( $document, $prepared_document ); + $diff = Health::diff_document_and_prepared_document( $document, $prepared_document ); $this->assertEquals( $expected_diff, $diff ); } @@ -265,14 +272,14 @@ public function test_diff_document_and_prepared_document( $prepared_document, $d * @dataProvider data_diff_document_and_prepared_document_does_not_generate_notices */ public function test_diff_document_and_prepared_document_does_not_generate_notices( array $document, array $prepared_document ): void { - self::assertNull( \Automattic\VIP\Search\Health::diff_document_and_prepared_document( $document, $prepared_document ) ); + self::assertNull( Health::diff_document_and_prepared_document( $document, $prepared_document ) ); } /** * @dataProvider data_diff_document_and_prepared_document_does_not_generate_notices */ public function test_simplified_diff_document_and_prepared_document_does_not_generate_notices( array $document, array $prepared_document ): void { - self::assertFalse( \Automattic\VIP\Search\Health::simplified_diff_document_and_prepared_document( $document, $prepared_document ) ); + self::assertFalse( Health::simplified_diff_document_and_prepared_document( $document, $prepared_document ) ); } public function data_diff_document_and_prepared_document_does_not_generate_notices(): iterable { @@ -312,7 +319,7 @@ public function data_diff_document_and_prepared_document_does_not_generate_notic } public function test_get_document_ids_for_batch() { - $ids = \Automattic\VIP\Search\Health::get_document_ids_for_batch( 1, 5 ); + $ids = Health::get_document_ids_for_batch( 1, 5 ); $expected_ids = array( 1, 2, 3, 4, 5 ); @@ -325,7 +332,7 @@ public function test_get_last_post_id() { $last_db_post_id = $post->ID; $last_es_post_id = 0; - $last_post_id = \Automattic\VIP\Search\Health::get_last_post_id(); + $last_post_id = Health::get_last_post_id(); $this->assertEquals( $last_post_id, max( $last_db_post_id, $last_es_post_id ) ); } @@ -333,7 +340,7 @@ public function test_get_last_post_id() { public function test_get_last_db_post_id() { $post = $this->factory()->post->create_and_get( [ 'post_status' => 'draft' ] ); - $last_post_id = \Automattic\VIP\Search\Health::get_last_db_post_id(); + $last_post_id = Health::get_last_db_post_id(); $this->assertEquals( $post->ID, $last_post_id ); } @@ -342,7 +349,7 @@ public function test_simplified_get_missing_docs_or_posts_diff() { $found_post_ids = array( 1, 3, 5 ); $found_document_ids = array( 1, 3, 7 ); - $diff = \Automattic\VIP\Search\Health::simplified_get_missing_docs_or_posts_diff( $found_post_ids, $found_document_ids ); + $diff = Health::simplified_get_missing_docs_or_posts_diff( $found_post_ids, $found_document_ids ); $expected_diff = array( 'post_5' => array( @@ -457,19 +464,20 @@ public function simplified_get_diff_document_and_prepared_document_data() { * @dataProvider simplified_get_diff_document_and_prepared_document_data */ public function test_simplified_diff_document_and_prepared_document( $prepared_document, $document, $expected_diff ) { - $diff = \Automattic\VIP\Search\Health::simplified_diff_document_and_prepared_document( $document, $prepared_document ); + $diff = Health::simplified_diff_document_and_prepared_document( $document, $prepared_document ); // Should be false since there are no inconsitencies in the test data $this->assertEquals( $diff, $expected_diff ); } public function test_get_index_entity_count_from_elastic_search__returns_result() { - $health = new \Automattic\VIP\Search\Health( self::$search_instance ); + $health = new Health( self::$search_instance ); $expected_count = 42; - /** @var \ElasticPress\Indexable&MockObject */ - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + /** @var Indexable&MockObject */ + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) + ->addMethods( self::$indexable_children_methods ) ->getMock(); $mocked_indexable->slug = 'foo'; @@ -487,11 +495,12 @@ public function test_get_index_entity_count_from_elastic_search__returns_result( } public function test_get_index_entity_count_from_elastic_search__exception() { - $health = new \Automattic\VIP\Search\Health( self::$search_instance ); + $health = new Health( self::$search_instance ); - /** @var \ElasticPress\Indexable&MockObject */ - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + /** @var Indexable&MockObject */ + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) + ->addMethods( self::$indexable_children_methods ) ->getMock(); $mocked_indexable->slug = 'foo'; @@ -505,11 +514,12 @@ public function test_get_index_entity_count_from_elastic_search__exception() { } public function test_get_index_entity_count_from_elastic_search__failed_query() { - $health = new \Automattic\VIP\Search\Health( self::$search_instance ); + $health = new Health( self::$search_instance ); - /** @var \ElasticPress\Indexable&MockObject */ - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + /** @var Indexable&MockObject */ + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) + ->addMethods( self::$indexable_children_methods ) ->getMock(); $mocked_indexable->slug = 'foo'; @@ -523,20 +533,20 @@ public function test_get_index_entity_count_from_elastic_search__failed_query() } public function test_validate_index_entity_count__failed_ES_should_pass_error() { - $error = new \WP_Error( 'test error' ); + $error = new WP_Error( 'test error' ); - /** @var \ElasticPress\Indexable&MockObject */ - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + /** @var Indexable&MockObject */ + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexable->slug = 'foo'; $mocked_indexable->method( 'index_exists' )->willReturn( true ); - /** @var \Automattic\VIP\Search\Health&MockObject */ - $patrtially_mocked_health = $this->getMockBuilder( \Automattic\VIP\Search\Health::class ) + /** @var Health&MockObject */ + $patrtially_mocked_health = $this->getMockBuilder( Health::class ) ->setConstructorArgs( [ self::$search_instance ] ) - ->setMethods( [ 'get_index_entity_count_from_elastic_search' ] ) + ->onlyMethods( [ 'get_index_entity_count_from_elastic_search' ] ) ->getMock(); $patrtially_mocked_health->method( 'get_index_entity_count_from_elastic_search' ) @@ -558,9 +568,9 @@ public function test_validate_index_entity_count__returns_all_data() { 'reason' => 'N/A', ]; - /** @var \ElasticPress\Indexable&MockObject */ - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + /** @var Indexable&MockObject */ + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexable->slug = $expected_result['entity']; @@ -570,10 +580,10 @@ public function test_validate_index_entity_count__returns_all_data() { ] ); $mocked_indexable->method( 'index_exists' )->willReturn( true ); - /** @var \Automattic\VIP\Search\Health&MockObject */ - $patrtially_mocked_health = $this->getMockBuilder( \Automattic\VIP\Search\Health::class ) + /** @var Health&MockObject */ + $patrtially_mocked_health = $this->getMockBuilder( Health::class ) ->setConstructorArgs( [ self::$search_instance ] ) - ->setMethods( [ 'get_index_entity_count_from_elastic_search' ] ) + ->onlyMethods( [ 'get_index_entity_count_from_elastic_search' ] ) ->getMock(); $patrtially_mocked_health->method( 'get_index_entity_count_from_elastic_search' ) @@ -595,17 +605,17 @@ public function test_validate_index_entity_count__skipping_non_initialized_index 'reason' => 'index-empty', ]; - /** @var \ElasticPress\Indexable&MockObject */ - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + /** @var Indexable&MockObject */ + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexable->method( 'index_exists' )->willReturn( true ); $mocked_indexable->slug = $expected_result['entity']; - /** @var \Automattic\VIP\Search\Health&MockObject */ - $patrtially_mocked_health = $this->getMockBuilder( \Automattic\VIP\Search\Health::class ) + /** @var Health&MockObject */ + $patrtially_mocked_health = $this->getMockBuilder( Health::class ) ->setConstructorArgs( [ self::$search_instance ] ) - ->setMethods( [ 'get_index_entity_count_from_elastic_search' ] ) + ->onlyMethods( [ 'get_index_entity_count_from_elastic_search' ] ) ->getMock(); $patrtially_mocked_health->method( 'get_index_entity_count_from_elastic_search' ) @@ -627,23 +637,23 @@ public function test_validate_index_entity_count__skipping_non_existing_indexes( 'reason' => 'index-not-found', ]; - /** @var \ElasticPress\Indexable&MockObject */ - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + /** @var Indexable&MockObject */ + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexable->method( 'index_exists' )->willReturn( false ); $mocked_indexable->slug = $expected_result['entity']; - $health = new \Automattic\VIP\Search\Health( self::$search_instance ); + $health = new Health( self::$search_instance ); $result = $health->validate_index_entity_count( [], $mocked_indexable ); $this->assertEquals( $result, $expected_result ); } public function test_validate_index_posts_content__ongoing_results_in_error() { - /** @var \Automattic\VIP\Search\Health&MockObject */ - $patrtially_mocked_health = $this->getMockBuilder( \Automattic\VIP\Search\Health::class ) - ->setMethods( [ 'is_validate_content_ongoing' ] ) + /** @var Health&MockObject */ + $patrtially_mocked_health = $this->getMockBuilder( Health::class ) + ->onlyMethods( [ 'is_validate_content_ongoing' ] ) ->disableOriginalConstructor() ->getMock(); @@ -656,20 +666,23 @@ public function test_validate_index_posts_content__ongoing_results_in_error() { } public function test_validate_index_posts_content__should_set_and_clear_lock() { - /** @var \Automattic\VIP\Search\Health&MockObject */ - $patrtially_mocked_health = $this->getMockBuilder( \Automattic\VIP\Search\Health::class ) - ->setMethods( [ 'set_validate_content_lock', 'remove_validate_content_lock', 'validate_index_posts_content_batch' ] ) + /** @var Health&MockObject */ + $patrtially_mocked_health = $this->getMockBuilder( Health::class ) + ->onlyMethods( [ 'set_validate_content_lock', 'remove_validate_content_lock', 'validate_index_posts_content_batch' ] ) ->disableOriginalConstructor() ->getMock(); $patrtially_mocked_health->method( 'validate_index_posts_content_batch' )->willReturn( [] ); - $mocked_indexables = $this->getMockBuilder( \ElasticPress\Indexables::class ) - ->setMethods( [ 'get' ] ) + /** @var Indexables&MockObject */ + $mocked_indexables = $this->getMockBuilder( Indexables::class ) + ->onlyMethods( [ 'get' ] ) ->getMock(); + $patrtially_mocked_health->indexables = $mocked_indexables; - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + /** @var Indexable&MockObject */ + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexables->method( 'get' )->willReturn( $mocked_indexable ); @@ -687,49 +700,51 @@ public function test_validate_index_posts_content__should_set_and_clear_last_pro 'batch_size' => 50, ]; - /** @var \Automattic\VIP\Search\Health&MockObject */ - $patrtially_mocked_health = $this->getMockBuilder( \Automattic\VIP\Search\Health::class ) - ->setMethods( [ 'update_validate_content_process', 'remove_validate_content_process', 'validate_index_posts_content_batch' ] ) + /** @var Health&MockObject */ + $patrtially_mocked_health = $this->getMockBuilder( Health::class ) + ->onlyMethods( [ 'update_validate_content_process', 'remove_validate_content_process', 'validate_index_posts_content_batch' ] ) ->disableOriginalConstructor() ->getMock(); $patrtially_mocked_health->method( 'validate_index_posts_content_batch' )->willReturn( [] ); - $mocked_indexables = $this->getMockBuilder( \ElasticPress\Indexables::class ) - ->setMethods( [ 'get' ] ) + $mocked_indexables = $this->getMockBuilder( Indexables::class ) + ->onlyMethods( [ 'get' ] ) ->getMock(); $patrtially_mocked_health->indexables = $mocked_indexables; - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexables->method( 'get' )->willReturn( $mocked_indexable ); $patrtially_mocked_health->expects( $this->exactly( 2 ) ) ->method( 'update_validate_content_process' ) - ->withConsecutive( [ $options['start_post_id'] ], [ $options['start_post_id'] + $options['batch_size'] ] ); + ->willReturnMap([ + [ $options['start_post_id'], null ], + [ $options['start_post_id'] + $options['batch_size'], null ], + ]); $patrtially_mocked_health->expects( $this->once() )->method( 'remove_validate_content_process' ); - $patrtially_mocked_health->validate_index_posts_content( $options ); } public function test_validate_index_posts_content__should_not_interact_with_process_if_parallel_run() { - /** @var \Automattic\VIP\Search\Health&MockObject */ - $patrtially_mocked_health = $this->getMockBuilder( \Automattic\VIP\Search\Health::class ) - ->setMethods( [ 'update_validate_content_process', 'remove_validate_content_process', 'validate_index_posts_content_batch' ] ) + /** @var Health&MockObject */ + $patrtially_mocked_health = $this->getMockBuilder( Health::class ) + ->onlyMethods( [ 'update_validate_content_process', 'remove_validate_content_process', 'validate_index_posts_content_batch' ] ) ->disableOriginalConstructor() ->getMock(); $patrtially_mocked_health->method( 'validate_index_posts_content_batch' )->willReturn( [] ); - $mocked_indexables = $this->getMockBuilder( \ElasticPress\Indexables::class ) - ->setMethods( [ 'get' ] ) + $mocked_indexables = $this->getMockBuilder( Indexables::class ) + ->onlyMethods( [ 'get' ] ) ->getMock(); $patrtially_mocked_health->indexables = $mocked_indexables; - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexables->method( 'get' )->willReturn( $mocked_indexable ); @@ -743,20 +758,20 @@ public function test_validate_index_posts_content__should_not_interact_with_proc } public function test_validate_index_posts_content__should_not_interact_with_process_if_non_default_start_id_is_sent_in() { - /** @var \Automattic\VIP\Search\Health&MockObject */ - $patrtially_mocked_health = $this->getMockBuilder( \Automattic\VIP\Search\Health::class ) - ->setMethods( [ 'update_validate_content_process', 'remove_validate_content_process', 'validate_index_posts_content_batch' ] ) + /** @var Health&MockObject */ + $patrtially_mocked_health = $this->getMockBuilder( Health::class ) + ->onlyMethods( [ 'update_validate_content_process', 'remove_validate_content_process', 'validate_index_posts_content_batch' ] ) ->disableOriginalConstructor() ->getMock(); $patrtially_mocked_health->method( 'validate_index_posts_content_batch' )->willReturn( [] ); - $mocked_indexables = $this->getMockBuilder( \ElasticPress\Indexables::class ) - ->setMethods( [ 'get' ] ) + $mocked_indexables = $this->getMockBuilder( Indexables::class ) + ->onlyMethods( [ 'get' ] ) ->getMock(); $patrtially_mocked_health->indexables = $mocked_indexables; - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexables->method( 'get' )->willReturn( $mocked_indexable ); @@ -773,21 +788,21 @@ public function test_validate_index_posts_content__pick_up_after_interuption() { $interrupted_post_id = 5; $start_post_id = 1; - /** @var \Automattic\VIP\Search\Health&MockObject */ - $patrtially_mocked_health = $this->getMockBuilder( \Automattic\VIP\Search\Health::class ) - ->setMethods( [ 'update_validate_content_process', 'remove_validate_content_process', 'get_validate_content_abandoned_process', 'validate_index_posts_content_batch' ] ) + /** @var Health&MockObject */ + $patrtially_mocked_health = $this->getMockBuilder( Health::class ) + ->onlyMethods( [ 'update_validate_content_process', 'remove_validate_content_process', 'get_validate_content_abandoned_process', 'validate_index_posts_content_batch' ] ) ->disableOriginalConstructor() ->getMock(); $patrtially_mocked_health->method( 'validate_index_posts_content_batch' )->willReturn( [] ); $patrtially_mocked_health->method( 'get_validate_content_abandoned_process' )->willReturn( $interrupted_post_id ); - $mocked_indexables = $this->getMockBuilder( \ElasticPress\Indexables::class ) - ->setMethods( [ 'get' ] ) + $mocked_indexables = $this->getMockBuilder( Indexables::class ) + ->onlyMethods( [ 'get' ] ) ->getMock(); $patrtially_mocked_health->indexables = $mocked_indexables; - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexables->method( 'get' )->willReturn( $mocked_indexable ); @@ -804,21 +819,21 @@ public function test_validate_index_posts_content__do_not_pick_up_after_interupt $interrupted_post_id = 5; $start_post_id = 1; - /** @var \Automattic\VIP\Search\Health&MockObject */ - $patrtially_mocked_health = $this->getMockBuilder( \Automattic\VIP\Search\Health::class ) - ->setMethods( [ 'update_validate_content_process', 'remove_validate_content_process', 'get_validate_content_abandoned_process', 'validate_index_posts_content_batch' ] ) + /** @var Health&MockObject */ + $patrtially_mocked_health = $this->getMockBuilder( Health::class ) + ->onlyMethods( [ 'update_validate_content_process', 'remove_validate_content_process', 'get_validate_content_abandoned_process', 'validate_index_posts_content_batch' ] ) ->disableOriginalConstructor() ->getMock(); $patrtially_mocked_health->method( 'validate_index_posts_content_batch' )->willReturn( [] ); $patrtially_mocked_health->method( 'get_validate_content_abandoned_process' )->willReturn( $interrupted_post_id ); - $mocked_indexables = $this->getMockBuilder( \ElasticPress\Indexables::class ) - ->setMethods( [ 'get' ] ) + $mocked_indexables = $this->getMockBuilder( Indexables::class ) + ->onlyMethods( [ 'get' ] ) ->getMock(); $patrtially_mocked_health->indexables = $mocked_indexables; - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexables->method( 'get' )->willReturn( $mocked_indexable ); @@ -837,21 +852,21 @@ public function test_validate_index_posts_content__do_not_pick_up_after_interupt $interrupted_post_id = 5; $start_post_id = 2; - /** @var \Automattic\VIP\Search\Health&MockObject */ - $patrtially_mocked_health = $this->getMockBuilder( \Automattic\VIP\Search\Health::class ) - ->setMethods( [ 'update_validate_content_process', 'remove_validate_content_process', 'get_validate_content_abandoned_process', 'validate_index_posts_content_batch' ] ) + /** @var Health&MockObject */ + $patrtially_mocked_health = $this->getMockBuilder( Health::class ) + ->onlyMethods( [ 'update_validate_content_process', 'remove_validate_content_process', 'get_validate_content_abandoned_process', 'validate_index_posts_content_batch' ] ) ->disableOriginalConstructor() ->getMock(); $patrtially_mocked_health->method( 'validate_index_posts_content_batch' )->willReturn( [] ); $patrtially_mocked_health->method( 'get_validate_content_abandoned_process' )->willReturn( $interrupted_post_id ); - $mocked_indexables = $this->getMockBuilder( \ElasticPress\Indexables::class ) - ->setMethods( [ 'get' ] ) + $mocked_indexables = $this->getMockBuilder( Indexables::class ) + ->onlyMethods( [ 'get' ] ) ->getMock(); $patrtially_mocked_health->indexables = $mocked_indexables; - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexables->method( 'get' )->willReturn( $mocked_indexable ); @@ -1004,22 +1019,23 @@ public function test_get_index_settings_diff_for_indexable( $actual, $desired, $ $mock_search = $this->createMock( Search::class ); $mock_search->versioning = $this->getMockBuilder( Versioning::class ) - ->setMethods( [ 'set_current_version_number', 'reset_current_version_number' ] ) + ->onlyMethods( [ 'set_current_version_number', 'reset_current_version_number' ] ) ->getMock(); $health = new Health( $mock_search ); - /** @var \ElasticPress\Indexable&MockObject */ - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + /** @var Indexable&MockObject */ + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexable->slug = 'post'; $mocked_indexable->method( 'index_exists' )->willReturn( true ); $mocked_indexable->method( 'get_index_name' )->willReturn( $index_name ); - $health->elasticsearch = $this->getMockBuilder( \ElasticPress\Elasticsearch::class ) - ->setMethods( [ 'get_index_settings' ] ) + /** @var Elasticsearch&MockObject */ + $health->elasticsearch = $this->getMockBuilder( Elasticsearch::class ) + ->onlyMethods( [ 'get_index_settings' ] ) ->getMock(); $health->elasticsearch->method( 'get_index_settings' ) @@ -1059,14 +1075,15 @@ public function test_get_index_settings_diff_for_indexable_without_index() { $mock_search = $this->createMock( Search::class ); $mock_search->versioning = $this->getMockBuilder( Versioning::class ) - ->setMethods( [ 'set_current_version_number', 'reset_current_version_number' ] ) + ->onlyMethods( [ 'set_current_version_number', 'reset_current_version_number' ] ) ->getMock(); $health = new Health( $mock_search ); - /** @var \ElasticPress\Indexable&MockObject */ - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + /** @var Indexable&MockObject */ + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) + ->addMethods( self::$indexable_children_methods ) ->getMock(); $mocked_indexable->slug = 'post'; @@ -1138,9 +1155,10 @@ public function test_heal_index_settings_for_indexable( $desired_settings, $opti /** @var Versioning&MockObject */ $versioning = $this->getMockBuilder( Versioning::class ) ->enableProxyingToOriginalMethods() - ->setMethods( [ 'get_current_version_number', 'set_current_version_number', 'reset_current_version_number' ] ) + ->onlyMethods( [ 'get_current_version_number', 'set_current_version_number', 'reset_current_version_number' ] ) ->getMock(); + /** @var Versioning&MockObject */ $mock_search->versioning = $versioning; // If we're healing a specific version, make sure we actually switch @@ -1157,9 +1175,9 @@ public function test_heal_index_settings_for_indexable( $desired_settings, $opti $health = new Health( $mock_search ); - /** @var \ElasticPress\Indexable&MockObject */ - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + /** @var Indexable&MockObject */ + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); $mocked_indexable->slug = 'post'; @@ -1170,8 +1188,9 @@ public function test_heal_index_settings_for_indexable( $desired_settings, $opti $mocked_indexable->method( 'generate_mapping' ) ->willReturn( [ 'settings' => $desired_settings ] ); - $health->elasticsearch = $this->getMockBuilder( \ElasticPress\Elasticsearch::class ) - ->setMethods( [ 'update_index_settings' ] ) + /** @var Elasticsearch&MockObject */ + $health->elasticsearch = $this->getMockBuilder( Elasticsearch::class ) + ->onlyMethods( [ 'update_index_settings' ] ) ->getMock(); $health->elasticsearch->method( 'update_index_settings' ) @@ -1424,7 +1443,7 @@ public function validate_post_index_mapping_data() { * @dataProvider validate_post_index_mapping_data */ public function test__validate_post_index_mapping( $index_name, $mapping, $expected_result ) { - $correct_mapping = \Automattic\VIP\Search\Health::validate_post_index_mapping( $index_name, $mapping ); + $correct_mapping = Health::validate_post_index_mapping( $index_name, $mapping ); $this->assertEquals( $expected_result, $correct_mapping ); } } diff --git a/tests/search/includes/classes/test-class-queue.php b/tests/search/includes/classes/test-class-queue.php index 4805844f0a..c3a345920f 100644 --- a/tests/search/includes/classes/test-class-queue.php +++ b/tests/search/includes/classes/test-class-queue.php @@ -2,7 +2,11 @@ namespace Automattic\VIP\Search; +use Automattic\VIP\Logstash\Logger; +use ElasticPress\Indexable\User\User; +use ElasticPress\Indexables; use PHPUnit\Framework\MockObject\MockObject; +use stdClass; use WP_UnitTestCase; use wpdb; @@ -14,10 +18,10 @@ */ class Queue_Test extends WP_UnitTestCase { - /** @var \Automattic\VIP\Search\Search */ + /** @var Search */ private $es; - /** @var \Automattic\VIP\Search\Queue */ + /** @var \Queue */ private $queue; public static function setUpBeforeClass(): void { @@ -28,13 +32,13 @@ public static function setUpBeforeClass(): void { require_once __DIR__ . '/../../../../search/search.php'; - \Automattic\VIP\Search\Search::instance()->init(); + Search::instance()->init(); // Required so that EP registers the Indexables do_action( 'plugins_loaded' ); // Users indexable doesn't get registered by default, but we have tests that queue user objects - \ElasticPress\Indexables::factory()->register( new \ElasticPress\Indexable\User\User() ); + Indexables::factory()->register( new User() ); } public function setUp(): void { @@ -46,7 +50,7 @@ public function setUp(): void { require_once __DIR__ . '/../../../../search/search.php'; - $this->es = \Automattic\VIP\Search\Search::instance(); + $this->es = Search::instance(); $this->es->init(); $this->queue = $this->es->queue; @@ -544,7 +548,7 @@ public function test_queue_objects_should_match_database() { $results = \wp_list_pluck( $wpdb->get_results( "SELECT object_id FROM `{$table_name}` WHERE 1" ), 'object_id' ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared - $this->assertEquals( $objects, $results, 'ids of objects sent to queue doesn\'t match ids of objects found in the database' ); + $this->assertEquals( $objects, $results, 'ids of objects sent to queue don\'t match ids of objects found in the database' ); } public function test_queue_objects_with_specific_index_version() { @@ -558,7 +562,7 @@ public function test_queue_objects_with_specific_index_version() { $results = \wp_list_pluck( $wpdb->get_results( "SELECT object_id FROM `{$table_name}` WHERE `index_version` = 2" ), 'object_id' ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared - $this->assertEquals( $objects, $results, 'ids of objects sent to queue doesn\'t match ids of objects found in the database' ); + $this->assertEquals( $objects, $results, 'ids of objects sent to queue don\'t match ids of objects found in the database' ); } public function test__action__ep_after_bulk_index_validation() { @@ -676,9 +680,9 @@ public function test_free_deadlocked_jobs_handle_duplicates() { 'index_version' => 1, ]; - /** @var MockObject&\Automattic\VIP\Search\Queue */ - $partially_mocked_queue = $this->getMockBuilder( \Automattic\VIP\Search\Queue::class ) - ->setMethods( [ + /** @var MockObject&Queue */ + $partially_mocked_queue = $this->getMockBuilder( Queue::class ) + ->onlyMethods( [ 'get_deadlocked_jobs', 'delete_jobs_on_the_already_queued_object', 'update_jobs', @@ -730,7 +734,7 @@ public function test__ratelimit_indexing_should_pass_bail_if_not_post() { * Ensure that the value passed into the filter is returned if the sync queue is empty */ public function test__ratelimit_indexing_should_pass_bail_if_sync_queue_empty() { - $sync_manager = new \stdClass(); + $sync_manager = new stdClass(); $sync_manager->sync_queue = array(); $this->assertTrue( $this->queue->ratelimit_indexing( true, $sync_manager, 'post' ), 'should return true since true was passed in' ); @@ -748,7 +752,7 @@ public function test_ratelimit_indexing_cache_count_should_not_exist_onload() { * Ensure that the count in the cache doesn't exist if the ratelimit_indexing returns early */ public function test_ratelimit_indexing_cache_count_should_not_exists_if_early_return() { - $sync_manager = new \stdClass(); + $sync_manager = new stdClass(); $sync_manager->sync_queue = array(); $this->queue->ratelimit_indexing( true, '', 'hippo' ); @@ -765,7 +769,7 @@ public function test_ratelimit_indexing_queue_should_be_empty_if_no_ratelimiting $table_name = $this->queue->schema->get_table_name(); - $sync_manager = new \stdClass(); + $sync_manager = new stdClass(); $sync_manager->sync_queue = range( 3, 9 ); $this->queue::$max_indexing_op_count = PHP_INT_MAX; // Ensure ratelimiting is disabled @@ -811,7 +815,7 @@ public function test_ratelimit_indexing_queue_should_be_populated_if_ratelimitin $table_name = $this->queue->schema->get_table_name(); - $sync_manager = new \stdClass(); + $sync_manager = new stdClass(); $sync_manager->sync_queue = range( 3, 9 ); $this->queue::$max_indexing_op_count = 0; // Ensure ratelimiting is enabled @@ -850,33 +854,32 @@ public function test_ratelimit_indexing_queue_should_be_populated_if_ratelimitin } public function test__ratelimit_indexing__handles_start_correctly() { - /** @var MockObject&\Automattic\VIP\Search\Queue */ - $partially_mocked_queue = $this->getMockBuilder( \Automattic\VIP\Search\Queue::class ) - ->setMethods( [ + /** @var MockObject&Queue */ + $partially_mocked_queue = $this->getMockBuilder( Queue::class ) + ->onlyMethods( [ 'handle_index_limiting_start_timestamp', 'maybe_alert_for_prolonged_index_limiting', - 'record_ratelimited_stat', 'intercept_ep_sync_manager_indexing', ] ) ->getMock(); - /** @var MockObject&\Automattic\VIP\Logstash\Logger */ - $partially_mocked_queue->logger = $this->getMockBuilder( \Automattic\VIP\Logstash\Logger::class ) - ->setMethods( [ 'log' ] ) - ->getMock(); + /** @var MockObject&Logger */ + $partially_mocked_queue->logger = $this->getMockBuilder( Logger::class ) + ->onlyMethods( [ 'log' ] ) + ->getMock(); $partially_mocked_queue->logger->expects( $this->once() ) - ->method( 'log' ) - ->with( - $this->equalTo( 'warning' ), - $this->equalTo( 'search_indexing_rate_limiting' ), - $this->equalTo( - 'Application 123 - http://example.org has triggered Elasticsearch indexing rate limiting, which will last for 300 seconds. Large batch indexing operations are being queued for indexing in batches over time.' - ), - $this->anything() - ); + ->method( 'log' ) + ->with( + $this->equalTo( 'warning' ), + $this->equalTo( 'search_indexing_rate_limiting' ), + $this->equalTo( + 'Application 123 - http://example.org has triggered Elasticsearch indexing rate limiting, which will last for 300 seconds. Large batch indexing operations are being queued for indexing in batches over time.' + ), + $this->anything() + ); - $sync_manager = new \stdClass(); + $sync_manager = new stdClass(); $sync_manager->sync_queue = range( 3, 9 ); $partially_mocked_queue::$max_indexing_op_count = 0; // Ensure ratelimiting is enabled @@ -888,16 +891,16 @@ public function test__ratelimit_indexing__handles_start_correctly() { } public function test__ratelimit_indexing__clears_start_correctly() { - /** @var MockObject&\Automattic\VIP\Search\Queue */ - $partially_mocked_queue = $this->getMockBuilder( \Automattic\VIP\Search\Queue::class ) - ->setMethods( [ + /** @var MockObject&Queue */ + $partially_mocked_queue = $this->getMockBuilder( Queue::class ) + ->onlyMethods( [ 'clear_index_limiting_start_timestamp', ] ) ->getMock(); $partially_mocked_queue->expects( $this->once() )->method( 'clear_index_limiting_start_timestamp' ); - $sync_manager = new \stdClass(); + $sync_manager = new stdClass(); $sync_manager->sync_queue = range( 3, 9 ); $partially_mocked_queue->ratelimit_indexing( true, $sync_manager, 'post' ); @@ -1264,20 +1267,21 @@ function () { } public function test__log_index_ratelimiting_start() { - $this->queue->logger = $this->getMockBuilder( \Automattic\VIP\Logstash\Logger::class ) - ->setMethods( [ 'log' ] ) - ->getMock(); + /** @var Logger&MockObject */ + $this->queue->logger = $this->getMockBuilder( Logger::class ) + ->onlyMethods( [ 'log' ] ) + ->getMock(); $this->queue->logger->expects( $this->once() ) - ->method( 'log' ) - ->with( - $this->equalTo( 'warning' ), - $this->equalTo( 'search_indexing_rate_limiting' ), - $this->equalTo( - 'Application 123 - http://example.org has triggered Elasticsearch indexing rate limiting, which will last for 300 seconds. Large batch indexing operations are being queued for indexing in batches over time.' - ), - $this->anything() - ); + ->method( 'log' ) + ->with( + $this->equalTo( 'warning' ), + $this->equalTo( 'search_indexing_rate_limiting' ), + $this->equalTo( + 'Application 123 - http://example.org has triggered Elasticsearch indexing rate limiting, which will last for 300 seconds. Large batch indexing operations are being queued for indexing in batches over time.' + ), + $this->anything() + ); $this->queue->log_index_ratelimiting_start(); } @@ -1333,7 +1337,7 @@ public function filter_index_exists_request_bad( $request, $query, $args, $failu protected static function get_method( $name ) { $class = new \ReflectionClass( __NAMESPACE__ . '\Queue' ); $method = $class->getMethod( $name ); - $method->setAccessible( true ); + $method->setAccessible( true ); // NOSONAR return $method; } diff --git a/tests/search/includes/classes/test-class-search.php b/tests/search/includes/classes/test-class-search.php index a8eb234c1f..4c3c5ad88d 100644 --- a/tests/search/includes/classes/test-class-search.php +++ b/tests/search/includes/classes/test-class-search.php @@ -5,6 +5,13 @@ use PHPUnit\Framework\MockObject\MockObject; use WP_UnitTestCase; use Automattic\Test\Constant_Mocker; +use Automattic\VIP\Utils\Alerts; +use ElasticPress\Features; +use ElasticPress\Indexable; +use ElasticPress\Indexables; +use stdClass; +use WP_Error; +use WP_Post; require_once __DIR__ . '/mock-header.php'; require_once __DIR__ . '/../../../../search/search.php'; @@ -27,12 +34,12 @@ public function setUp(): void { parent::setUp(); \Automattic\VIP\Prometheus\Plugin::get_instance()->init_registry(); - $this->search_instance = new \Automattic\VIP\Search\Search(); + $this->search_instance = new Search(); $this->search_instance->load_collector(); \Automattic\VIP\Prometheus\Plugin::get_instance()->load_collectors(); self::$mock_global_functions = $this->getMockBuilder( self::class ) - ->setMethods( [ 'mock_vip_safe_wp_remote_request', 'mock_wp_remote_request' ] ) + ->addMethods( [ 'mock_vip_safe_wp_remote_request', 'mock_wp_remote_request' ] ) ->getMock(); header_remove(); @@ -41,7 +48,7 @@ public function setUp(): void { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler set_error_handler( static function ( int $errno, string $errstr ): never { // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- CLI - throw new \Exception( $errstr, $errno ); + throw new \Exception( $errstr, $errno ); // NOSONAR }, E_USER_WARNING ); } @@ -67,7 +74,7 @@ public function test_query_es_with_invalid_type() { public function test__vip_search_filter_ep_index_name() { $this->init_es(); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); $index_name = apply_filters( 'ep_index_name', 'index-name', 1, $indexable ); @@ -82,7 +89,7 @@ public function test__vip_search_filter_ep_index_name() { public function test__vip_search_filter_ep_index_name_global_index() { $this->init_es(); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); $index_name = apply_filters( 'ep_index_name', 'index-name', null, $indexable ); @@ -269,11 +276,11 @@ public function test__is_url_query_cacheable( $query, $args, $expected_is_cachea public function test__vip_search_filter_ep_index_name_with_versions( $current_version, $blog_id, $expected_index_name ) { $this->init_es(); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); // Mock the Versioning class so we can control which version it returns - $stub = $this->getMockBuilder( \Automattic\VIP\Search\Versioning::class ) - ->setMethods( [ 'get_current_version_number' ] ) + $stub = $this->getMockBuilder( Versioning::class ) + ->onlyMethods( [ 'get_current_version_number' ] ) ->getMock(); $stub->expects( $this->once() ) @@ -294,7 +301,7 @@ public function test__vip_search_filter_ep_index_name_with_overridden_version() Constant_Mocker::define( 'FILES_CLIENT_SITE_ID', 123 ); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); add_filter( 'ep_do_intercept_request', [ $this, 'filter_ok_es_requests' ], PHP_INT_MAX, 5 ); @@ -303,7 +310,7 @@ public function test__vip_search_filter_ep_index_name_with_overridden_version() remove_filter( 'ep_do_intercept_request', [ $this, 'filter_ok_es_requests' ], PHP_INT_MAX ); $this->assertNotFalse( $new_version, 'Failed to add new version of index' ); - $this->assertNotInstanceOf( \WP_Error::class, $new_version, 'Got WP_Error when adding new index version' ); + $this->assertNotInstanceOf( WP_Error::class, $new_version, 'Got WP_Error when adding new index version' ); // Override the version $override_result = $this->search_instance->versioning->set_current_version_number( $indexable, 2 ); @@ -327,7 +334,7 @@ public function test__vip_search_filter_ep_index_name_with_overridden_version() public function test__vip_search_filter__ep_global_alias() { $this->init_es(); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); $alias_name = $indexable->get_network_alias(); @@ -354,7 +361,7 @@ public function test__vip_search_filter_filter__ep_post_mapping__large_site() { return $counts; }; - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); add_filter( 'wp_count_posts', $return_big_count ); @@ -376,12 +383,12 @@ public function test__vip_search_filter_filter__ep_user_mapping__large_site() { $this->init_es(); // Activate and set-up the feature - \ElasticPress\Features::factory()->activate_feature( 'users' ); - \ElasticPress\Features::factory()->setup_features(); + Features::factory()->activate_feature( 'users' ); + Features::factory()->setup_features(); // Simulate a large site - $return_big_count = function ( $counts ) { - $counts = new \stdClass(); + $return_big_count = function () { + $counts = new stdClass(); $counts->avail_roles = 100; $counts->total_users = 3000000; @@ -390,7 +397,7 @@ public function test__vip_search_filter_filter__ep_user_mapping__large_site() { add_filter( 'pre_count_users', $return_big_count ); - $indexable = \ElasticPress\Indexables::factory()->get( 'user' ); + $indexable = Indexables::factory()->get( 'user' ); if ( method_exists( $indexable, 'build_settings' ) ) { $settings = $indexable->build_settings(); } else { @@ -425,12 +432,12 @@ public function test__vip_search_enforces_disabled_features( $slug ) { $this->init_es(); // Activate the feature - \ElasticPress\Features::factory()->activate_feature( $slug ); + Features::factory()->activate_feature( $slug ); // And attempt to force-enable it via filter add_filter( 'ep_feature_active', '__return_true' ); - $active = \ElasticPress\Features::factory()->get_registered_feature( $slug ); + $active = Features::factory()->get_registered_feature( $slug ); $this->assertFalse( $active ); } @@ -692,7 +699,6 @@ public function test__vip_search_filter__ep_enable_query_integration_during_inde * Test for making sure the round robin function returns the next array value */ public function test__vip_search_get_next_host() { - $es = new \Automattic\VIP\Search\Search(); Constant_Mocker::define( 'VIP_ELASTICSEARCH_ENDPOINTS', array( 'test0', @@ -718,7 +724,6 @@ public function test__vip_search_get_random_host() { 'test2', 'test3', ); - $es = new \Automattic\VIP\Search\Search(); $this->assertContains( $this->search_instance->get_random_host( $hosts ), $hosts ); } @@ -952,7 +957,7 @@ public function test__vip_search_filter__ep_allow_post_content_filtered_index() * Ensure that is_query_integration_enabled() is false by default with no options/constants */ public function test__is_query_integration_enabled_default() { - $this->assertFalse( \Automattic\VIP\Search\Search::is_query_integration_enabled() ); + $this->assertFalse( Search::is_query_integration_enabled() ); } /* @@ -964,7 +969,7 @@ public function test__is_query_integration_enabled_default() { public function test__is_query_integration_enabled_via_option() { update_option( 'vip_enable_vip_search_query_integration', true ); - $this->assertTrue( \Automattic\VIP\Search\Search::is_query_integration_enabled() ); + $this->assertTrue( Search::is_query_integration_enabled() ); delete_option( 'vip_enable_vip_search_query_integration' ); } @@ -978,7 +983,7 @@ public function test__is_query_integration_enabled_via_option() { public function test__is_query_integration_enabled_via_legacy_constant() { Constant_Mocker::define( 'VIP_ENABLE_ELASTICSEARCH_QUERY_INTEGRATION', true ); - $this->assertTrue( \Automattic\VIP\Search\Search::is_query_integration_enabled() ); + $this->assertTrue( Search::is_query_integration_enabled() ); } /* @@ -990,7 +995,7 @@ public function test__is_query_integration_enabled_via_legacy_constant() { public function test__is_query_integration_enabled_via_constant() { Constant_Mocker::define( 'VIP_ENABLE_VIP_SEARCH_QUERY_INTEGRATION', true ); - $this->assertTrue( \Automattic\VIP\Search\Search::is_query_integration_enabled() ); + $this->assertTrue( Search::is_query_integration_enabled() ); } /** @@ -1001,13 +1006,13 @@ public function test__is_query_integration_enabled_via_constant() { */ public function test__is_query_integration_enabled_via_query_param() { // Set es query string to test override - $_GET[ \Automattic\VIP\Search\Search::QUERY_INTEGRATION_FORCE_ENABLE_KEY ] = true; + $_GET[ Search::QUERY_INTEGRATION_FORCE_ENABLE_KEY ] = true; - $this->assertTrue( \Automattic\VIP\Search\Search::is_query_integration_enabled() ); + $this->assertTrue( Search::is_query_integration_enabled() ); } public function test_is_network_mode_default() { - $this->assertFalse( \Automattic\VIP\Search\Search::is_network_mode() ); + $this->assertFalse( Search::is_network_mode() ); } /** @@ -1017,7 +1022,7 @@ public function test_is_network_mode_default() { public function test_is_network_mode_with_constant() { Constant_Mocker::define( 'EP_IS_NETWORK', true ); - $this->assertTrue( \Automattic\VIP\Search\Search::is_network_mode() ); + $this->assertTrue( Search::is_network_mode() ); } /** @@ -1027,7 +1032,7 @@ public function test_is_network_mode_with_constant() { public function test_is_network_mode_with_constant_false() { Constant_Mocker::define( 'EP_IS_NETWORK', false ); - $this->assertFalse( \Automattic\VIP\Search\Search::is_network_mode() ); + $this->assertFalse( Search::is_network_mode() ); } /* @@ -1043,7 +1048,7 @@ public function test__ep_skip_query_integration_filter() { // We pass in `true` as the starting value for the filter, indicating it should be skipped. We expect that `true` comes back out, // even though query integration is enabled, which indicates that we're properly respecting other filters that have already decided // this query should be skipped - $this->assertTrue( \Automattic\VIP\Search\Search::ep_skip_query_integration( true ) ); + $this->assertTrue( Search::ep_skip_query_integration( true ) ); } /* @@ -1053,19 +1058,19 @@ public function test__ep_skip_query_integration_filter() { * @preserveGlobalState disabled */ public function test__ep_skip_query_integration_default() { - $this->assertTrue( \Automattic\VIP\Search\Search::ep_skip_query_integration( false ) ); + $this->assertTrue( Search::ep_skip_query_integration( false ) ); } /* * Ensure ratelimiting works properly with ep_skip_query_integration filter */ public function test__rate_limit_ep_query_integration__triggers() { - $es = new \Automattic\VIP\Search\Search(); + $es = new Search(); $es->init(); add_option( 'vip_enable_vip_search_query_integration', true ); Constant_Mocker::define( 'VIP_ENABLE_VIP_SEARCH_QUERY_INTEGRATION', true ); - $_GET[ \Automattic\VIP\Search\Search::QUERY_INTEGRATION_FORCE_ENABLE_KEY ] = true; + $_GET[ Search::QUERY_INTEGRATION_FORCE_ENABLE_KEY ] = true; $this->assertFalse( $es->rate_limit_ep_query_integration( false ), 'the default value should be false' ); $this->assertTrue( $es->rate_limit_ep_query_integration( true ), 'should honor filters that skip query integrations' ); @@ -1083,9 +1088,9 @@ public function test__rate_limit_ep_query_integration__triggers() { } public function test__rate_limit_ep_query_integration__handles_start_correctly() { - /** @var MockObject&\Automattic\VIP\Search\Search */ - $partially_mocked_search = $this->getMockBuilder( \Automattic\VIP\Search\Search::class ) - ->setMethods( [ 'handle_query_limiting_start_timestamp', 'maybe_alert_for_prolonged_query_limiting' ] ) + /** @var MockObject&Search */ + $partially_mocked_search = $this->getMockBuilder( Search::class ) + ->onlyMethods( [ 'handle_query_limiting_start_timestamp', 'maybe_alert_for_prolonged_query_limiting' ] ) ->getMock(); $partially_mocked_search->init(); @@ -1107,7 +1112,7 @@ public function test__rate_limit_ep_query_integration__handles_start_correctly() * Ensure we don't load es-wp-query by default (if it's not enabled) */ public function test__should_load_es_wp_query_default() { - $should = \Automattic\VIP\Search\Search::should_load_es_wp_query(); + $should = Search::should_load_es_wp_query(); $this->assertFalse( $should ); } @@ -1123,7 +1128,7 @@ public function test__should_load_es_wp_query_already_loaded() { $this->setExpectedIncorrectUsage( 'Automattic\VIP\Search\Search::should_load_es_wp_query' ); - $should = \Automattic\VIP\Search\Search::should_load_es_wp_query(); + $should = Search::should_load_es_wp_query(); $this->assertFalse( $should ); } @@ -1137,7 +1142,7 @@ public function test__should_load_es_wp_query_already_loaded() { public function test__should_load_es_wp_query_query_integration() { Constant_Mocker::define( 'VIP_ENABLE_VIP_SEARCH_QUERY_INTEGRATION', true ); - $should = \Automattic\VIP\Search\Search::should_load_es_wp_query(); + $should = Search::should_load_es_wp_query(); $this->assertTrue( $should ); } @@ -1159,8 +1164,6 @@ public function test__query_count_incr() { } public function test__truncate_search_string_length__user_no_cap() { - $es = new \Automattic\VIP\Search\Search(); - $expected_search_string = '1nAtu5t4QRo9XmU5VeKFOCTfQN62FrbvvoQXkU1782KOThAlt50NipM7V4dZNGG4eO54HsOQlJaBPStX'; $provided_search_string = '1nAtu5t4QRo9XmU5VeKFOCTfQN62FrbvvoQXkU1782KOThAlt50NipM7V4dZNGG4eO54HsOQlJaBPStXPRoxWPHqdrHGsGkNQJJshYseaePxCJuGmY7kYp941TUoNF3GhSBEzjajNu0iwdCWrPMLxSJ5XXBltNM9of2LKvwa1hNPOXLka1tyAi8PSZlS53RbGhv7egKOYPyyPpR6mZlzJhx6nXXlZ5t3BtRdQOIvGho6HjdYwdd1hMyHHv1qpggg5oMk1nWsx5fJ0B3bAFYKt1Y5dOA0Q4lQUqj8mf1LjcmR73wQwujc1GQfgCKj9X9Ktr6LrDtN5zAJFQboAJa7fZ9AiGxbJqUrLFs1nAtu5t4QRo9XmU5VeKFOCTfQN62FrbvvoQXkU1782KOThAlt50NipM7V4dZNGG4eO54HsOQlJaBPStXPRoxWPHqdrHGsGkNQJJshYseaePxCJuGmY7kYp941TUoNF3GhSBEzjajNu0iwdCWrPMLxSJ5XXBltNM9of2LKvwa1hNPOXLka1tyAi8PSZlS53RbGhv7egKOYPyyPpR6mZlzJhx6nXXlZ5t3BtRdQOIvGho6HjdYwdd1hMyHHv1qpggg5oMk1nWsx5fJ0B3bAFYKt1Y5dOA0Q4lQUqj8mf1LjcmR73wQwujc1GQfgCKj9X9Ktr6LrDtN5zAJFQboAJa7fZ9AiGxbJqUrLFs88'; @@ -1183,8 +1186,6 @@ public function test__truncate_search_string_length__user_with_cap() { wp_set_current_user( $admin_user ); - $es = new \Automattic\VIP\Search\Search(); - $expected_search_string = '1nAtu5t4QRo9XmU5VeKFOCTfQN62FrbvvoQXkU1782KOThAlt50NipM7V4dZNGG4eO54HsOQlJaBPStXPRoxWPHqdrHGsGkNQJJshYseaePxCJuGmY7kYp941TUoNF3GhSBEzjajNu0iwdCWrPMLxSJ5XXBltNM9of2LKvwa1hNPOXLka1tyAi8PSZlS53RbGhv7egKOYPyyPpR6mZlzJhx6nXXlZ5t3BtRdQOIvGho6HjdYwdd1hMyHHv1qpggg5oMk1nWsx5fJ0B3bAFYKt1Y5dOA0Q4lQUqj8mf1LjcmR73wQwujc1GQfgCKj9X9Ktr6LrDtN5zAJFQboAJa7fZ9AiGxbJqUrLFs1nAtu5t4QRo9XmU5VeKFOCTfQN62FrbvvoQXkU1782KOThAlt50NipM7V4dZNGG4eO54HsOQlJaBPStXPRoxWPHqdrHGsGkNQJJshYseaePxCJuGmY7kYp941TUoNF3GhSBEzjajNu0iwdCWrPMLxSJ5XXB'; $provided_search_string = '1nAtu5t4QRo9XmU5VeKFOCTfQN62FrbvvoQXkU1782KOThAlt50NipM7V4dZNGG4eO54HsOQlJaBPStXPRoxWPHqdrHGsGkNQJJshYseaePxCJuGmY7kYp941TUoNF3GhSBEzjajNu0iwdCWrPMLxSJ5XXBltNM9of2LKvwa1hNPOXLka1tyAi8PSZlS53RbGhv7egKOYPyyPpR6mZlzJhx6nXXlZ5t3BtRdQOIvGho6HjdYwdd1hMyHHv1qpggg5oMk1nWsx5fJ0B3bAFYKt1Y5dOA0Q4lQUqj8mf1LjcmR73wQwujc1GQfgCKj9X9Ktr6LrDtN5zAJFQboAJa7fZ9AiGxbJqUrLFs1nAtu5t4QRo9XmU5VeKFOCTfQN62FrbvvoQXkU1782KOThAlt50NipM7V4dZNGG4eO54HsOQlJaBPStXPRoxWPHqdrHGsGkNQJJshYseaePxCJuGmY7kYp941TUoNF3GhSBEzjajNu0iwdCWrPMLxSJ5XXBltNM9of2LKvwa1hNPOXLka1tyAi8PSZlS53RbGhv7egKOYPyyPpR6mZlzJhx6nXXlZ5t3BtRdQOIvGho6HjdYwdd1hMyHHv1qpggg5oMk1nWsx5fJ0B3bAFYKt1Y5dOA0Q4lQUqj8mf1LjcmR73wQwujc1GQfgCKj9X9Ktr6LrDtN5zAJFQboAJa7fZ9AiGxbJqUrLFs88'; @@ -1205,8 +1206,6 @@ public function test__truncate_search_string_length__user_with_cap() { public function test__limit_field_limit_absolute_maximum_is_20000() { $this->setExpectedIncorrectUsage( 'limit_field_limit' ); - $es = new \Automattic\VIP\Search\Search(); - $this->assertEquals( 20000, $this->search_instance->limit_field_limit( 1000000 ) ); } @@ -1215,8 +1214,6 @@ public function test__limit_field_limit_absolute_maximum_is_20000() { * @preserveGlobalState disabled */ public function test__limit_field_limit_should_respect_values_under_maximum() { - $es = new \Automattic\VIP\Search\Search(); - $this->assertEquals( 777, $this->search_instance->limit_field_limit( 777 ) ); } @@ -1287,7 +1284,7 @@ public function get_filter__ep_sync_taxonomies_default_data() { public function test__filter__ep_sync_taxonomies_default( $input_taxonomies ) { $this->init_es(); - $post = new \stdClass(); + $post = new stdClass(); $filtered_taxonomies = apply_filters( 'ep_sync_taxonomies', $input_taxonomies, $post ); @@ -1305,7 +1302,7 @@ public function test__filter__ep_sync_taxonomies_default( $input_taxonomies ) { public function test__filter__ep_sync_taxonomies_added() { $this->init_es(); - $post = new \stdClass(); + $post = new stdClass(); $start_taxonomies = array( (object) array( @@ -1344,7 +1341,7 @@ function ( $taxonomies ) { public function test__filter__ep_sync_taxonomies_removed() { $this->init_es(); - $post = new \stdClass(); + $post = new stdClass(); $start_taxonomies = array( (object) array( @@ -1408,8 +1405,6 @@ public function test__is_jetpack_migration__different_value() { * @preserveGlobalState disabled */ public function test__filter__ep_prepare_meta_data_allow_list_should_be_respected_by_default() { - $es = new \Automattic\VIP\Search\Search(); - \add_filter( 'vip_search_post_meta_allow_list', function () { @@ -1436,7 +1431,7 @@ function () { $post_meta['random_thing_not_allow_listed'] = array( 'Missing' ); - $post = new \WP_Post( new \StdClass() ); + $post = new WP_Post( new StdClass() ); $post->ID = 0; $meta = $this->search_instance->filter__ep_prepare_meta_data( $post_meta, $post ); @@ -1451,7 +1446,7 @@ function () { * @preserveGlobalState disabled */ public function test__filter__ep_prepare_meta_data_allow_list_should_be_respected_by_default_assoc() { - $es = new \Automattic\VIP\Search\Search(); + $es = new Search(); \add_filter( 'vip_search_post_meta_allow_list', @@ -1491,7 +1486,7 @@ function () { $post_meta['random_thing_not_allow_listed'] = array( 'Missing' ); - $post = new \WP_Post( new \StdClass() ); + $post = new WP_Post( new StdClass() ); $post->ID = 0; $meta = $es->filter__ep_prepare_meta_data( $post_meta, $post ); @@ -1524,7 +1519,7 @@ public function test__filter__ep_indexable_mapping() { $this->init_es(); // Should apply to all indexables - $indexables = \ElasticPress\Indexables::factory()->get_all(); + $indexables = Indexables::factory()->get_all(); // Make sure the above worked $this->assertNotEmpty( $indexables, 'Indexables array was empty' ); @@ -1550,7 +1545,7 @@ public function test__filter__ep_indexable_mapping_invalid_datacenter() { $this->init_es(); // Should apply to all indexables - $indexables = \ElasticPress\Indexables::factory()->get_all(); + $indexables = Indexables::factory()->get_all(); // Make sure the above worked $this->assertNotEmpty( $indexables, 'Indexables array was empty' ); @@ -1714,7 +1709,7 @@ public function test__get_post_meta_allow_list__combinations_for_jetpack_migrati remove_all_filters( 'jetpack_sync_post_meta_whitelist' ); $this->init_es(); - $post = new \WP_Post( new \StdClass() ); + $post = new WP_Post( new StdClass() ); $post->ID = 0; if ( is_array( $vip_search_keys ) ) { @@ -1775,7 +1770,7 @@ public function get_post_meta_allow_list__combinations_not_jetpack_migration_dat public function test__get_post_meta_allow_list__combinations_not_jetpack_migration( $vip_search_keys, $jetpack_added, $expected ) { $this->init_es(); - $post = new \WP_Post( new \StdClass() ); + $post = new WP_Post( new StdClass() ); $post->ID = 0; if ( is_array( $vip_search_keys ) ) { @@ -1826,7 +1821,7 @@ public function get_post_meta_allow_list__processing_array_data() { public function test__get_post_meta_allow_list__processing_array( $returned_by_filter, $expected ) { $this->init_es(); - $post = new \WP_Post( new \StdClass() ); + $post = new WP_Post( new StdClass() ); $post->ID = 0; // clearing up jetpack values as those are put by default to vip_search_post_meta_allow_list but are not the object of testing here @@ -1991,7 +1986,7 @@ public function filter__ep_prepare_meta_allowed_protected_keys__should_use_post_ * @dataProvider filter__ep_prepare_meta_allowed_protected_keys__should_use_post_meta_allow_list_data */ public function test__filter__ep_prepare_meta_allowed_protected_keys__should_use_post_meta_allow_list( $default_ep_protected_keys, $added_keys, $expected ) { - $post = new \WP_Post( new \StdClass() ); + $post = new WP_Post( new StdClass() ); $post->ID = 0; // clearing up jetpack values as those are put by default to vip_search_post_meta_allow_list but are not the object of testing here @@ -2001,7 +1996,7 @@ public function test__filter__ep_prepare_meta_allowed_protected_keys__should_use return array_merge( $meta_keys, $added_keys ); }, 0); - \Automattic\VIP\Search\Search::instance(); + Search::instance(); $result = \apply_filters( 'ep_prepare_meta_allowed_protected_keys', $default_ep_protected_keys, $post ); @@ -2017,19 +2012,18 @@ public function test__maybe_alert_for_average_queue_time__sends_notification() { $expected_message = "Average index queue wait time for application {$application_id} - {$application_url} is currently {$average_queue_value} seconds. There are {$queue_count_value} items in the queue and the oldest item is {$longest_queue_value} seconds old"; $expected_level = 2; - $es = new \Automattic\VIP\Search\Search(); $this->search_instance->init(); - $alerts_mocked = $this->createMock( \Automattic\VIP\Utils\Alerts::class ); - $queue_mocked = $this->createMock( \Automattic\VIP\Search\Queue::class ); - $indexables_mock = $this->createMock( \ElasticPress\Indexables::class ); + $alerts_mocked = $this->createMock( Alerts::class ); + $queue_mocked = $this->createMock( Queue::class ); + $indexables_mock = $this->createMock( Indexables::class ); $this->search_instance->queue = $queue_mocked; $this->search_instance->indexables = $indexables_mock; $this->search_instance->alerts = $alerts_mocked; $indexables_mock->method( 'get' ) - ->willReturn( $this->createMock( \ElasticPress\Indexable::class ) ); + ->willReturn( $this->createMock( Indexable::class ) ); $queue_mocked ->method( 'get_queue_stats' ) @@ -2062,14 +2056,14 @@ public function test__maybe_alert_for_field_count( $field_count, $should_alert ) $expected_message = "The field count for post index for application $application_id - $application_url is too damn high - $field_count"; $expected_level = 2; - /** @var MockObject&\Automattic\VIP\Search\Search */ - $partially_mocked_search = $this->getMockBuilder( \Automattic\VIP\Search\Search::class ) - ->setMethods( [ 'get_current_field_count' ] ) + /** @var MockObject&Search */ + $partially_mocked_search = $this->getMockBuilder( Search::class ) + ->onlyMethods( [ 'get_current_field_count' ] ) ->getMock(); $partially_mocked_search->init(); - $alerts_mocked = $this->createMock( \Automattic\VIP\Utils\Alerts::class ); - $indexables_mock = $this->createMock( \ElasticPress\Indexables::class ); + $alerts_mocked = $this->createMock( Alerts::class ); + $indexables_mock = $this->createMock( Indexables::class ); $partially_mocked_search->indexables = $indexables_mock; $partially_mocked_search->alerts = $alerts_mocked; @@ -2110,11 +2104,10 @@ public function test__maybe_alert_for_prolonged_query_limiting( $difference, $sh wp_cache_set( Search::QUERY_RATE_LIMITED_START_CACHE_KEY, $query_limited_start, Search::SEARCH_CACHE_GROUP ); } - $es = new \Automattic\VIP\Search\Search(); $this->search_instance->init(); $this->search_instance->set_time( $time ); - $alerts_mocked = $this->createMock( \Automattic\VIP\Utils\Alerts::class ); + $alerts_mocked = $this->createMock( Alerts::class ); $this->search_instance->alerts = $alerts_mocked; @@ -2215,7 +2208,7 @@ public function stat_sampling_invalid_stat_param_data() { return [ [ array() ], [ null ], - [ new \stdClass() ], + [ new stdClass() ], [ 5 ], [ 8.6 ], ]; @@ -2225,7 +2218,7 @@ public function stat_sampling_invalid_value_param_data() { return [ [ array() ], [ null ], - [ new \stdClass() ], + [ new stdClass() ], [ 'random' ], ]; } @@ -2268,17 +2261,17 @@ public function test__ep_handle_failed_request__log_message( $response, $expecte $this->init_es(); $this->search_instance->logger = $this->getMockBuilder( \Automattic\VIP\Logstash\Logger::class ) - ->setMethods( [ 'log' ] ) - ->getMock(); + ->onlyMethods( [ 'log' ] ) + ->getMock(); $this->search_instance->logger->expects( $this->once() ) - ->method( 'log' ) - ->with( - $this->equalTo( 'error' ), - $this->equalTo( 'search_query_error' ), - $this->equalTo( $expected_message ), - $this->anything() - ); + ->method( 'log' ) + ->with( + $this->equalTo( 'error' ), + $this->equalTo( 'search_query_error' ), + $this->equalTo( $expected_message ), + $this->anything() + ); $this->search_instance->ep_handle_failed_request( null, $response, [], null, null, '' ); } @@ -2290,8 +2283,8 @@ public function test__ep_handle_failed_request__skiplist() { $this->init_es(); $this->search_instance->logger = $this->getMockBuilder( \Automattic\VIP\Logstash\Logger::class ) - ->setMethods( [ 'log' ] ) - ->getMock(); + ->onlyMethods( [ 'log' ] ) + ->getMock(); $this->search_instance->logger->expects( $this->never() )->method( 'log' ); @@ -2363,8 +2356,8 @@ public function test__maybe_log_query_ratelimiting_start_should_do_nothing_if_ra wp_cache_set( $this->search_instance::QUERY_RATE_LIMITED_START_CACHE_KEY, time(), $this->search_instance::SEARCH_CACHE_GROUP ); $this->search_instance->logger = $this->getMockBuilder( \Automattic\VIP\Logstash\Logger::class ) - ->setMethods( [ 'log' ] ) - ->getMock(); + ->onlyMethods( [ 'log' ] ) + ->getMock(); $this->search_instance->logger->expects( $this->never() )->method( 'log' ); @@ -2375,32 +2368,31 @@ public function test__maybe_log_query_ratelimiting_start_should_log_if_ratelimit $this->init_es(); $this->search_instance->logger = $this->getMockBuilder( \Automattic\VIP\Logstash\Logger::class ) - ->setMethods( [ 'log' ] ) - ->getMock(); + ->onlyMethods( [ 'log' ] ) + ->getMock(); $this->search_instance->logger->expects( $this->once() ) - ->method( 'log' ) - ->with( - $this->equalTo( 'warning' ), - $this->equalTo( 'search_query_rate_limiting' ), - $this->equalTo( - 'Application 123 - http://example.org has triggered Elasticsearch query rate-limiting, which will last up to 300 seconds. Subsequent or repeat occurrences are possible. Half of traffic is diverted to the database when queries are rate-limited.' - ), - $this->anything() - ); + ->method( 'log' ) + ->with( + $this->equalTo( 'warning' ), + $this->equalTo( 'search_query_rate_limiting' ), + $this->equalTo( + 'Application 123 - http://example.org has triggered Elasticsearch query rate-limiting, which will last up to 300 seconds. Subsequent or repeat occurrences are possible. Half of traffic is diverted to the database when queries are rate-limited.' + ), + $this->anything() + ); $this->search_instance->maybe_log_query_ratelimiting_start(); } public function test__add_attachment_to_ep_indexable_post_types_should_return_the_passed_value_if_not_array() { Constant_Mocker::define( 'EP_DASHBOARD_SYNC', 'test' ); - $es = new \Automattic\VIP\Search\Search(); $this->search_instance->init(); $this->assertEquals( 'testing', $this->search_instance->add_attachment_to_ep_indexable_post_types( 'testing' ) ); $this->assertEquals( 65, $this->search_instance->add_attachment_to_ep_indexable_post_types( 65 ) ); $this->assertEquals( null, $this->search_instance->add_attachment_to_ep_indexable_post_types( null ) ); - $this->assertEquals( new \StdClass(), $this->search_instance->add_attachment_to_ep_indexable_post_types( new \StdClass() ) ); + $this->assertEquals( new StdClass(), $this->search_instance->add_attachment_to_ep_indexable_post_types( new StdClass() ) ); } public function test__add_attachment_to_ep_indexable_post_types_should_append_attachment_to_array() { @@ -2425,21 +2417,21 @@ public function test__add_attachment_to_ep_indexable_post_types_should_append_at public function test__ep_indexable_post_types_should_return_the_passed_value_if_not_array() { $this->init_es(); - \ElasticPress\Features::factory()->activate_feature( 'protected_content' ); + Features::factory()->activate_feature( 'protected_content' ); $this->assertEquals( 'testing', apply_filters( 'ep_indexable_post_types', 'testing' ) ); $this->assertEquals( 65, apply_filters( 'ep_indexable_post_types', 65 ) ); $this->assertEquals( null, apply_filters( 'ep_indexable_post_types', null ) ); - $this->assertEquals( new \StdClass(), apply_filters( 'ep_indexable_post_types', new \StdClass() ) ); + $this->assertEquals( new StdClass(), apply_filters( 'ep_indexable_post_types', new StdClass() ) ); } public function test__ep_indexable_post_types_should_append_attachment_to_array() { // Ensure ElasticPress is ready do_action( 'plugins_loaded' ); - \ElasticPress\Features::factory()->activate_feature( 'protected_content' ); + Features::factory()->activate_feature( 'protected_content' ); - $es = new \Automattic\VIP\Search\Search(); + $es = new Search(); $es->init(); $this->assertEquals( array( 'attachment' => 'attachment' ), apply_filters( 'ep_indexable_post_types', array() ) ); @@ -2468,7 +2460,7 @@ public function test__is_protected_content_enabled_should_return_false_if_protec public function test__is_protected_content_enabled_should_return_true_if_protected_content_enabled() { $this->init_es(); - \ElasticPress\Features::factory()->activate_feature( 'protected_content' ); + Features::factory()->activate_feature( 'protected_content' ); $this->assertTrue( $this->search_instance->is_protected_content_enabled() ); } @@ -2557,7 +2549,7 @@ public function test__limit_max_result_window( $input, $expected ) { } public function test__are_es_constants_defined__no_constatns() { - $result = \Automattic\VIP\Search\Search::are_es_constants_defined(); + $result = Search::are_es_constants_defined(); $this->assertFalse( $result ); } @@ -2567,7 +2559,7 @@ public function test__are_es_constants_defined__all_constatns() { Constant_Mocker::define( 'VIP_ELASTICSEARCH_USERNAME', 'foo' ); Constant_Mocker::define( 'VIP_ELASTICSEARCH_PASSWORD', 'bar' ); - $result = \Automattic\VIP\Search\Search::are_es_constants_defined(); + $result = Search::are_es_constants_defined(); $this->assertTrue( $result ); } @@ -2577,7 +2569,7 @@ public function test__are_es_constants_defined__empty_password() { Constant_Mocker::define( 'VIP_ELASTICSEARCH_USERNAME', 'foo' ); Constant_Mocker::define( 'VIP_ELASTICSEARCH_PASSWORD', '' ); - $result = \Automattic\VIP\Search\Search::are_es_constants_defined(); + $result = Search::are_es_constants_defined(); $this->assertFalse( $result ); } @@ -2586,7 +2578,7 @@ public function test__are_es_constants_defined__no_username() { Constant_Mocker::define( 'VIP_ELASTICSEARCH_ENDPOINTS', [ 'endpoint' ] ); Constant_Mocker::define( 'VIP_ELASTICSEARCH_PASSWORD', 'bar' ); - $result = \Automattic\VIP\Search\Search::are_es_constants_defined(); + $result = Search::are_es_constants_defined(); $this->assertFalse( $result ); } @@ -2596,7 +2588,7 @@ public function test__are_es_constants_defined__no_endpoints() { Constant_Mocker::define( 'VIP_ELASTICSEARCH_USERNAME', 'foo' ); Constant_Mocker::define( 'VIP_ELASTICSEARCH_PASSWORD', 'bar' ); - $result = \Automattic\VIP\Search\Search::are_es_constants_defined(); + $result = Search::are_es_constants_defined(); $this->assertFalse( $result ); } @@ -2640,7 +2632,7 @@ public function test__filter_ep_enable_do_weighting__no_custom_search_results() $this->search_instance->init(); - \ElasticPress\Features::factory()->activate_feature( 'searchordering' ); + Features::factory()->activate_feature( 'searchordering' ); update_option( 'vip_custom_results_existence', '0' ); $this->assertFalse( apply_filters( 'ep_enable_do_weighting', true, [], [], [] ) ); @@ -2652,7 +2644,7 @@ public function test__filter_ep_enable_do_weighting__custom_search_results() { $this->search_instance->init(); - \ElasticPress\Features::factory()->activate_feature( 'searchordering' ); + Features::factory()->activate_feature( 'searchordering' ); update_option( 'vip_custom_results_existence', '1' ); $this->assertTrue( apply_filters( 'ep_enable_do_weighting', true, [], [], [] ) ); @@ -2664,7 +2656,7 @@ public function test__set_custom_results_existence_cache() { $this->search_instance->init(); - \ElasticPress\Features::factory()->activate_feature( 'searchordering' ); + Features::factory()->activate_feature( 'searchordering' ); $post = wp_insert_post( [ 'post_type' => 'ep-pointer', @@ -2678,7 +2670,7 @@ public function test__set_custom_results_existence_cache() { $this->assertEquals( '0', get_option( 'vip_custom_results_existence' ) ); - $post2 = wp_insert_post( [ + wp_insert_post( [ 'post_type' => 'ep-pointer', 'post_status' => 'publish', 'post_title' => 'Test CSR 2', @@ -2699,7 +2691,7 @@ public function test__set_custom_results_existence_cache() { protected static function get_method( $name ) { $class = new \ReflectionClass( __NAMESPACE__ . '\Search' ); $method = $class->getMethod( $name ); - $method->setAccessible( true ); + $method->setAccessible( true ); // NOSONAR return $method; } @@ -2710,19 +2702,11 @@ protected static function get_property( $name ) { $class = new \ReflectionClass( __NAMESPACE__ . '\Search' ); $property = $class->getProperty( $name ); - $property->setAccessible( true ); + $property->setAccessible( true ); // NOSONAR return $property; } - public function mock_vip_safe_wp_remote_request() { - /* Empty */ - } - - public function mock_wp_remote_request() { - /* Empty */ - } - /** * Helper function to set required constant, initialize the search instance, and do required action for setting up EP indexables. * diff --git a/tests/search/includes/classes/test-class-settingshealthjob.php b/tests/search/includes/classes/test-class-settingshealthjob.php index caf4edc4d3..5b7102581e 100644 --- a/tests/search/includes/classes/test-class-settingshealthjob.php +++ b/tests/search/includes/classes/test-class-settingshealthjob.php @@ -4,10 +4,15 @@ use WP_UnitTestCase; use Automattic\Test\Constant_Mocker; +use ElasticPress\Indexable; +use ElasticPress\Indexables; +use PHPUnit\Framework\MockObject\MockObject; +use WP_Error; class SettingsHealthJob_Test extends WP_UnitTestCase { - /** @var \Automattic\VIP\Search\Search */ + /** @var Search */ public static $search; + /** @var Versioning */ public static $version_instance; public static function setUpBeforeClass(): void { @@ -16,7 +21,7 @@ public static function setUpBeforeClass(): void { require_once __DIR__ . '/../../../../search/includes/classes/class-settingshealthjob.php'; require_once __DIR__ . '/../../../../prometheus.php'; - self::$search = \Automattic\VIP\Search\Search::instance(); + self::$search = Search::instance(); self::$search->init(); if ( ! Constant_Mocker::defined( 'VIP_ELASTICSEARCH_ENDPOINTS' ) ) { // Need to define endpoints for versioning usage @@ -47,11 +52,12 @@ public function setUp(): void { } public function test__process_indexables_settings_health_results__reports_error() { - $error = new \WP_Error( 'foo', 'Bar' ); + $error = new WP_Error( 'foo', 'Bar' ); - $stub = $this->getMockBuilder( \Automattic\VIP\Search\SettingsHealthJob::class ) + /** @var MockObject&SettingsHealthJob */ + $stub = $this->getMockBuilder( SettingsHealthJob::class ) ->disableOriginalConstructor() - ->setMethods( [ 'send_alert' ] ) + ->onlyMethods( [ 'send_alert' ] ) ->getMock(); $stub->expects( $this->once() ) @@ -61,15 +67,16 @@ public function test__process_indexables_settings_health_results__reports_error( } public function test__process_indexables_settings_health_results__reports_error_per_indexable() { - $error = new \WP_Error( 'foo', 'Bar' ); + $error = new WP_Error( 'foo', 'Bar' ); $unhealthy_indexables = [ 'post' => $error, 'user' => $error, ]; - $stub = $this->getMockBuilder( \Automattic\VIP\Search\SettingsHealthJob::class ) + /** @var MockObject&SettingsHealthJob */ + $stub = $this->getMockBuilder( SettingsHealthJob::class ) ->disableOriginalConstructor() - ->setMethods( [ 'send_alert' ] ) + ->onlyMethods( [ 'send_alert' ] ) ->getMock(); $stub->expects( $this->exactly( count( $unhealthy_indexables ) ) ) @@ -79,18 +86,19 @@ public function test__process_indexables_settings_health_results__reports_error_ } public function test__heal_index_settings__reports_error_per_failed_indexable_retrieval() { - $error = new \WP_Error( 'foo', 'Bar' ); + $error = new WP_Error( 'foo', 'Bar' ); $unhealthy_indexables = [ 'post' => [], 'user' => [], ]; - $indexables_mock = $this->createMock( \ElasticPress\Indexables::class ); + $indexables_mock = $this->createMock( Indexables::class ); $indexables_mock->method( 'get' )->willReturn( $error ); - $stub = $this->getMockBuilder( \Automattic\VIP\Search\SettingsHealthJob::class ) + /** @var MockObject&SettingsHealthJob */ + $stub = $this->getMockBuilder( SettingsHealthJob::class ) ->disableOriginalConstructor() - ->setMethods( [ 'send_alert' ] ) + ->onlyMethods( [ 'send_alert' ] ) ->getMock(); $stub->indexables = $indexables_mock; @@ -126,13 +134,13 @@ public function test__heal_index_settings__heal_indexables_with_diff() { ], ]; - $indexables_mock = $this->createMock( \ElasticPress\Indexables::class ); - $indexables_mock->method( 'get' )->willReturn( $this->createMock( \ElasticPress\Indexable::class ) ); + $indexables_mock = $this->createMock( Indexables::class ); + $indexables_mock->method( 'get' )->willReturn( $this->createMock( Indexable::class ) ); - - $health_mock = $this->getMockBuilder( \Automattic\VIP\Search\Health::class ) + /** @var MockObject&Health */ + $health_mock = $this->getMockBuilder( Health::class ) ->disableOriginalConstructor() - ->setMethods( [ 'heal_index_settings_for_indexable' ] ) + ->onlyMethods( [ 'heal_index_settings_for_indexable' ] ) ->getMock(); $health_mock->method( 'heal_index_settings_for_indexable' )->willReturn( array( @@ -141,9 +149,10 @@ public function test__heal_index_settings__heal_indexables_with_diff() { 'index_name' => 'foo-index', ) ); - $stub = $this->getMockBuilder( \Automattic\VIP\Search\SettingsHealthJob::class ) + /** @var MockObject&SettingsHealthJob */ + $stub = $this->getMockBuilder( SettingsHealthJob::class ) ->disableOriginalConstructor() - ->setMethods( [ 'send_alert' ] ) + ->onlyMethods( [ 'send_alert' ] ) ->getMock(); $stub->indexables = $indexables_mock; @@ -156,11 +165,12 @@ public function test__heal_index_settings__heal_indexables_with_diff() { } public function test__maybe_process_build__one_version_existence() { - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); - $stub = $this->getMockBuilder( \Automattic\VIP\Search\SettingsHealthJob::class ) + /** @var MockObject&SettingsHealthJob */ + $stub = $this->getMockBuilder( SettingsHealthJob::class ) ->disableOriginalConstructor() - ->setMethods( [ 'wp_schedule_single_event', 'send_alert' ] ) + ->onlyMethods( [ 'send_alert' ] ) ->getMock(); $stub->search = self::$search; @@ -170,18 +180,19 @@ public function test__maybe_process_build__one_version_existence() { $stub->maybe_process_build( $indexable ); - $event = \wp_next_scheduled( \Automattic\VIP\Search\SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug ] ); + $event = wp_next_scheduled( SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug ] ); $this->assertIsInt( $event ); } public function test__maybe_process_build__two_version_existence() { - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); self::$version_instance->add_version( $indexable ); - $stub = $this->getMockBuilder( \Automattic\VIP\Search\SettingsHealthJob::class ) + /** @var MockObject&SettingsHealthJob */ + $stub = $this->getMockBuilder( SettingsHealthJob::class ) ->disableOriginalConstructor() - ->setMethods( [ 'wp_schedule_single_event', 'send_alert' ] ) + ->onlyMethods( [ 'send_alert' ] ) ->getMock(); $stub->search = self::$search; @@ -191,18 +202,19 @@ public function test__maybe_process_build__two_version_existence() { $stub->maybe_process_build( $indexable ); - $event = \wp_next_scheduled( \Automattic\VIP\Search\SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug ] ); + $event = wp_next_scheduled( SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug ] ); $this->assertFalse( $event ); } public function test__maybe_process_build__locks() { - update_option( \Automattic\VIP\Search\SettingsHealthJob::BUILD_LOCK_NAME, time() ); + update_option( SettingsHealthJob::BUILD_LOCK_NAME, time() ); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); - $stub = $this->getMockBuilder( \Automattic\VIP\Search\SettingsHealthJob::class ) + /** @var MockObject&SettingsHealthJob */ + $stub = $this->getMockBuilder( SettingsHealthJob::class ) ->disableOriginalConstructor() - ->setMethods( [ 'send_alert' ] ) + ->onlyMethods( [ 'send_alert' ] ) ->getMock(); $stub->search = self::$search; @@ -212,23 +224,24 @@ public function test__maybe_process_build__locks() { $stub->maybe_process_build( $indexable ); - $event = \wp_next_scheduled( \Automattic\VIP\Search\SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug ] ); + $event = wp_next_scheduled( SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug ] ); $this->assertFalse( $event ); - delete_option( \Automattic\VIP\Search\SettingsHealthJob::BUILD_LOCK_NAME ); + delete_option( SettingsHealthJob::BUILD_LOCK_NAME ); $stub->maybe_process_build( $indexable ); - $event = \wp_next_scheduled( \Automattic\VIP\Search\SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug ] ); + $event = wp_next_scheduled( SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug ] ); $this->assertIsInt( $event ); } public function test__maybe_process_build() { - update_option( \Automattic\VIP\Search\SettingsHealthJob::BUILD_LOCK_NAME, time() ); + update_option( SettingsHealthJob::BUILD_LOCK_NAME, time() ); - $stub = $this->getMockBuilder( \Automattic\VIP\Search\SettingsHealthJob::class ) + /** @var MockObject&SettingsHealthJob */ + $stub = $this->getMockBuilder( SettingsHealthJob::class ) ->disableOriginalConstructor() - ->setMethods( [ 'check_process_build' ] ) + ->onlyMethods( [ 'check_process_build' ] ) ->getMock(); $stub->search = self::$search; @@ -236,15 +249,16 @@ public function test__maybe_process_build() { $stub->expects( $this->once() ) ->method( 'check_process_build' ); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); - $status = $stub->maybe_process_build( $indexable ); + $indexable = Indexables::factory()->get( 'post' ); + $stub->maybe_process_build( $indexable ); } public function test__maybe_process_build__in_progress() { - update_option( \Automattic\VIP\Search\SettingsHealthJob::BUILD_LOCK_NAME, time() ); + update_option( SettingsHealthJob::BUILD_LOCK_NAME, time() ); - $stub = $this->getMockBuilder( \Automattic\VIP\Search\SettingsHealthJob::class ) - ->setMethods( [ 'check_process_build' ] ) + /** @var MockObject&SettingsHealthJob */ + $stub = $this->getMockBuilder( SettingsHealthJob::class ) + ->onlyMethods( [ 'check_process_build' ] ) ->disableOriginalConstructor() ->getMock(); @@ -253,20 +267,21 @@ public function test__maybe_process_build__in_progress() { $stub->method( 'check_process_build' ) ->willReturn( 'in-progress' ); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); - $status = $stub->maybe_process_build( $indexable ); + $indexable = Indexables::factory()->get( 'post' ); + $stub->maybe_process_build( $indexable ); - $event = \wp_next_scheduled( \Automattic\VIP\Search\SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug ] ); + $event = wp_next_scheduled( SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug ] ); $this->assertFalse( $event ); } public function test__maybe_process_build__resume() { - update_option( \Automattic\VIP\Search\SettingsHealthJob::BUILD_LOCK_NAME, time() ); + update_option( SettingsHealthJob::BUILD_LOCK_NAME, time() ); $last_processed_id = '1234'; - update_option( \Automattic\VIP\Search\SettingsHealthJob::LAST_PROCESSED_ID_OPTION, $last_processed_id ); + update_option( SettingsHealthJob::LAST_PROCESSED_ID_OPTION, $last_processed_id ); - $stub = $this->getMockBuilder( \Automattic\VIP\Search\SettingsHealthJob::class ) - ->setMethods( [ 'check_process_build' ] ) + /** @var MockObject&SettingsHealthJob */ + $stub = $this->getMockBuilder( SettingsHealthJob::class ) + ->onlyMethods( [ 'check_process_build' ] ) ->disableOriginalConstructor() ->getMock(); @@ -275,21 +290,22 @@ public function test__maybe_process_build__resume() { $stub->method( 'check_process_build' ) ->willReturn( 'resume' ); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); - $status = $stub->maybe_process_build( $indexable ); + $indexable = Indexables::factory()->get( 'post' ); + $stub->maybe_process_build( $indexable ); - $event = \wp_next_scheduled( \Automattic\VIP\Search\SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug, $last_processed_id ] ); + $event = wp_next_scheduled( SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug, $last_processed_id ] ); $this->assertIsInt( $event ); } public function test__maybe_process_build__swap() { - update_option( \Automattic\VIP\Search\SettingsHealthJob::BUILD_LOCK_NAME, time() ); + update_option( SettingsHealthJob::BUILD_LOCK_NAME, time() ); $completed_status = 'Indexing completed'; - update_option( \Automattic\VIP\Search\SettingsHealthJob::LAST_PROCESSED_ID_OPTION, $completed_status ); + update_option( SettingsHealthJob::LAST_PROCESSED_ID_OPTION, $completed_status ); - $stub = $this->getMockBuilder( \Automattic\VIP\Search\SettingsHealthJob::class ) + /** @var MockObject&SettingsHealthJob */ + $stub = $this->getMockBuilder( SettingsHealthJob::class ) ->disableOriginalConstructor() - ->setMethods( [ 'check_process_build', 'alert_to_swap_index_versions' ] ) + ->onlyMethods( [ 'check_process_build', 'alert_to_swap_index_versions' ] ) ->getMock(); $stub->search = self::$search; @@ -300,10 +316,10 @@ public function test__maybe_process_build__swap() { $stub->expects( $this->once() ) ->method( 'alert_to_swap_index_versions' ); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); - $status = $stub->maybe_process_build( $indexable ); + $indexable = Indexables::factory()->get( 'post' ); + $stub->maybe_process_build( $indexable ); - $event = \wp_next_scheduled( \Automattic\VIP\Search\SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug, $completed_status ] ); + $event = wp_next_scheduled( SettingsHealthJob::CRON_EVENT_BUILD_NAME, [ $indexable->slug, $completed_status ] ); $this->assertFalse( $event ); } } diff --git a/tests/search/includes/classes/test-class-versioning.php b/tests/search/includes/classes/test-class-versioning.php index b37f101291..bef1c787d8 100644 --- a/tests/search/includes/classes/test-class-versioning.php +++ b/tests/search/includes/classes/test-class-versioning.php @@ -6,6 +6,10 @@ use WP_Error; use WP_UnitTestCase; use Automattic\Test\Constant_Mocker; +use Automattic\VIP\Utils\Alerts; +use ElasticPress\Elasticsearch; +use ElasticPress\Indexable; +use ElasticPress\Indexables; // phpcs:disable Squiz.PHP.CommentedOutCode.Found -- false positives @@ -21,15 +25,11 @@ class Versioning_Test extends WP_UnitTestCase { private static $indexable_methods = [ 'query_es', 'query_db', - 'format_args', 'get_mapping', 'prepare_document', 'put_mapping', - 'build_mapping', 'index_exists', 'get_index_name', - 'get_index_settings', - 'update_index_settings', ]; public function mock_http_response( $mocked_response ) { @@ -70,7 +70,7 @@ public static function setUpBeforeClass(): void { add_filter( 'ep_intercept_remote_request', '__return_true' ); - if ( method_exists( \ElasticPress\Indexable::class, 'build_settings' ) ) { + if ( method_exists( Indexable::class, 'build_settings' ) ) { self::$indexable_methods[] = 'build_settings'; } else { self::$indexable_methods[] = 'generate_mapping'; @@ -206,7 +206,7 @@ public function get_active_version_data() { // Indexable slug 'post', // Expected active version - new \WP_Error( 'no-active-version', 'No active version found' ), + new WP_Error( 'no-active-version', 'No active version found' ), ), // No versions tracked @@ -243,7 +243,7 @@ public function get_active_version_data() { * @dataProvider get_active_version_data */ public function test_get_active_version_number( $versions, $indexable_slug, $expected_active_version ) { - $indexable = \ElasticPress\Indexables::factory()->get( $indexable_slug ); + $indexable = Indexables::factory()->get( $indexable_slug ); self::$version_instance->update_versions( $indexable, $versions ); @@ -336,7 +336,7 @@ public function get_inactive_versions_data() { * @dataProvider get_inactive_versions_data */ public function test_get_inactive_versions( $versions, $indexable_slug, $expected_inactive_versions ) { - $indexable = \ElasticPress\Indexables::factory()->get( $indexable_slug ); + $indexable = Indexables::factory()->get( $indexable_slug ); self::$version_instance->update_versions( $indexable, $versions ); @@ -465,7 +465,7 @@ public function normalize_version_number_data() { // Version string to be normalized 'previous', // Expected normalized version number - new \WP_Error( 'no-previous-version' ), + new WP_Error( 'no-previous-version' ), ), // No next @@ -486,7 +486,7 @@ public function normalize_version_number_data() { // Version string to be normalized 'next', // Expected normalized version number - new \WP_Error( 'no-next-version' ), + new WP_Error( 'no-next-version' ), ), // No active @@ -507,7 +507,7 @@ public function normalize_version_number_data() { // Version string to be normalized 'active', // Expected active version - new \WP_Error( 'no-active-version' ), + new WP_Error( 'no-active-version' ), ), // No active, trying to get next @@ -528,7 +528,7 @@ public function normalize_version_number_data() { // Version string to be normalized 'next', // Expected active version - new \WP_Error( 'no-active-index-found' ), + new WP_Error( 'no-active-index-found' ), ), // Regular, 'inactive' array( @@ -579,7 +579,7 @@ public function normalize_version_number_data() { // Version string to be normalized 'inactive', // Expected inactive version - new \WP_Error( 'no-inactive-versions-found' ), + new WP_Error( 'no-inactive-versions-found' ), ), ); } @@ -588,7 +588,7 @@ public function normalize_version_number_data() { * @dataProvider normalize_version_number_data */ public function test_normalize_version_number( $versions, $indexable_slug, $version_string, $expected_version_number ) { - $indexable = \ElasticPress\Indexables::factory()->get( $indexable_slug ); + $indexable = Indexables::factory()->get( $indexable_slug ); self::$version_instance->update_versions( $indexable, $versions ); @@ -671,7 +671,7 @@ public function add_version_data() { * @dataProvider add_version_data */ public function test_add_version( $versions, $indexable_slug, $expected_new_versions ) { - $indexable = \ElasticPress\Indexables::factory()->get( $indexable_slug ); + $indexable = Indexables::factory()->get( $indexable_slug ); self::$version_instance->update_versions( $indexable, $versions ); @@ -829,7 +829,7 @@ public function activate_version_data() { * @dataProvider activate_version_data */ public function test_activate_version( $versions, $indexable_slug, $version_to_activate, $expected_new_versions ) { - $indexable = \ElasticPress\Indexables::factory()->get( $indexable_slug ); + $indexable = Indexables::factory()->get( $indexable_slug ); self::$version_instance->update_versions( $indexable, $versions ); @@ -899,7 +899,7 @@ public function activate_version_invalid_data() { * @dataProvider activate_version_invalid_data */ public function test_activate_version_invalid( $versions, $indexable_slug, $version_to_activate ) { - $indexable = \ElasticPress\Indexables::factory()->get( $indexable_slug ); + $indexable = Indexables::factory()->get( $indexable_slug ); self::$version_instance->update_versions( $indexable, $versions ); @@ -964,7 +964,7 @@ public function deactivate_version_data() { // Version to deactivate 2, // Expected - new \WP_Error( 'inactive-index-version-already', 'The index version 2 already inactive' ), + new WP_Error( 'inactive-index-version-already', 'The index version 2 already inactive' ), ), // With no indexes array( @@ -975,7 +975,7 @@ public function deactivate_version_data() { // Version to deactivate 2, // Expected - new \WP_Error( 'invalid-index-version', 'The index version 2 was not found' ), + new WP_Error( 'invalid-index-version', 'The index version 2 was not found' ), ), ); } @@ -984,7 +984,7 @@ public function deactivate_version_data() { * @dataProvider deactivate_version_data */ public function test_deactivate_version( $versions, $indexable_slug, $version_to_deactivate, $expected_new_versions ) { - $indexable = \ElasticPress\Indexables::factory()->get( $indexable_slug ); + $indexable = Indexables::factory()->get( $indexable_slug ); self::$version_instance->update_versions( $indexable, $versions ); @@ -1075,10 +1075,10 @@ public function delete_version_data() { * @dataProvider delete_version_data */ public function test_delete_version( $start_versions, $indexable_slug, $version_number, $expected_versions ) { - $indexable = \ElasticPress\Indexables::factory()->get( $indexable_slug ); + $indexable = Indexables::factory()->get( $indexable_slug ); $indexable_mock = $this->getMockBuilder( get_class( $indexable ) ) - ->setMethods( [ 'delete_index' ] ) + ->onlyMethods( [ 'delete_index' ] ) ->getMock(); $indexable_mock->expects( $this->once() ) @@ -1100,7 +1100,7 @@ public function test_delete_version( $start_versions, $indexable_slug, $version_ } public function test_delete_version_invalid() { - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); $result = self::$version_instance->delete_version( $indexable, 99999 ); @@ -1109,7 +1109,7 @@ public function test_delete_version_invalid() { } public function test_delete_version_while_active() { - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); $active_version_number = self::$version_instance->get_active_version_number( $indexable ); @@ -1122,7 +1122,7 @@ public function test_delete_version_while_active() { public function test_current_version_number_overrides() { delete_option( Versioning::INDEX_VERSIONS_OPTION ); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); $this->setup_ok_es_requests(); @@ -1131,7 +1131,7 @@ public function test_current_version_number_overrides() { $this->clean_up_ok_es_requests(); $this->assertNotFalse( $result, 'Failed to add new version of index' ); - $this->assertNotInstanceOf( \WP_Error::class, $result, 'Got WP_Error when adding new index version' ); + $this->assertNotInstanceOf( WP_Error::class, $result, 'Got WP_Error when adding new index version' ); // Defaults to active index (1 in our case) $this->assertEquals( 1, self::$version_instance->get_current_version_number( $indexable ), 'Default (non-overridden) version number is wrong' ); @@ -1188,8 +1188,8 @@ public function test_queue_job_replication() { self::$search->queue->empty_queue(); // For these tests, we're just using the post type and index versions 1, 2, and 3, for simplicity - self::$version_instance->update_versions( \ElasticPress\Indexables::factory()->get( 'post' ), array() ); // Reset them - self::$version_instance->add_version( \ElasticPress\Indexables::factory()->get( 'post' ) ); + self::$version_instance->update_versions( Indexables::factory()->get( 'post' ), array() ); // Reset them + self::$version_instance->add_version( Indexables::factory()->get( 'post' ) ); do_action( 'vip_search_indexing_object_queued', 1, 'post', array( 'foo' => 'bar' ), 1 ); do_action( 'vip_search_indexing_object_queued', 2, 'post', array( 'foo' => 'bar' ), 1 ); @@ -1323,8 +1323,8 @@ public function test_replicate_queued_objects_to_other_versions( $input, $expect self::$search->queue->empty_queue(); // For these tests, we're just using the post type and index versions 1, 2, and 3, for simplicity - self::$version_instance->update_versions( \ElasticPress\Indexables::factory()->get( 'post' ), array() ); // Reset them - self::$version_instance->add_version( \ElasticPress\Indexables::factory()->get( 'post' ) ); + self::$version_instance->update_versions( Indexables::factory()->get( 'post' ), array() ); // Reset them + self::$version_instance->add_version( Indexables::factory()->get( 'post' ) ); $queue_table_name = self::$search->queue->schema->get_table_name(); @@ -1351,10 +1351,10 @@ public function test_replicate_indexed_objects_to_other_versions() { self::$search->queue->empty_queue(); // For these tests, we're just using the post type and index versions 1, 2, and 3, for simplicity - self::$version_instance->update_versions( \ElasticPress\Indexables::factory()->get( 'post' ), array() ); // Reset them - self::$version_instance->add_version( \ElasticPress\Indexables::factory()->get( 'post' ) ); + self::$version_instance->update_versions( Indexables::factory()->get( 'post' ), array() ); // Reset them + self::$version_instance->add_version( Indexables::factory()->get( 'post' ) ); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); $sync_manager = $indexable->sync_manager; @@ -1407,7 +1407,7 @@ public function test_replicate_indexed_objects_to_other_versions() { } public function test_replicate_deletes_to_other_index_versions() { - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); // For these tests, we're just using the post type and index versions 1, 2, and 3, for simplicity self::$version_instance->update_versions( $indexable, array() ); // Reset them @@ -1479,7 +1479,7 @@ public function test_normalize_version( $input, $expected ) { } public function test_get_version() { - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); $one = self::$version_instance->get_version( $indexable, 1 ); @@ -1552,7 +1552,7 @@ public function get_versions_default_data() { */ public function test__get_versions_default( $versioning, $expected ) { update_option( Versioning::INDEX_VERSIONS_OPTION, $versioning ); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); $result = self::$version_instance->get_versions( $indexable ); @@ -1607,7 +1607,7 @@ public function get_versions_data() { */ public function test__get_versions( $versioning, $expected ) { update_option( Versioning::INDEX_VERSIONS_OPTION, $versioning ); - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); $result = self::$version_instance->get_versions( $indexable, false ); @@ -1673,7 +1673,7 @@ public function test__get_versions__combine_globals( $slug, $global, $expected ) update_option( Versioning::INDEX_VERSIONS_OPTION, $this->get_versions__combine_globals_local ); update_site_option( Versioning::INDEX_VERSIONS_OPTION_GLOBAL, $this->get_versions__combine_globals_global ); - $indexable_mock = $this->getMockBuilder( \ElasticPress\Indexable::class )->getMock(); + $indexable_mock = $this->getMockBuilder( Indexable::class )->getMock(); $indexable_mock->slug = $slug; $indexable_mock->global = $global; @@ -1720,19 +1720,19 @@ public function maybe_self_heal_reconstruct_data() { */ public function test__maybe_self_heal_reconstruct( $indexables, $versioning, $expected_reconstructions ) { $indexables_mocks = array_map( function ( $slug ) { - $indexable_mock = $this->getMockBuilder( \ElasticPress\Indexable::class )->getMock(); + $indexable_mock = $this->getMockBuilder( Indexable::class )->getMock(); $indexable_mock->slug = $slug; return $indexable_mock; }, $indexables); - $indexables_mock = $this->getMockBuilder( \ElasticPress\Indexables::class ) - ->setMethods( [ 'get_all' ] ) + $indexables_mock = $this->getMockBuilder( Indexables::class ) + ->onlyMethods( [ 'get_all' ] ) ->getMock(); $indexables_mock->method( 'get_all' )->willReturn( $indexables_mocks ); - /** @var MockObject&\Automattic\VIP\Search\Versioning */ - $partially_mocked_versioning = $this->getMockBuilder( \Automattic\VIP\Search\Versioning::class ) - ->setMethods( [ 'get_versions', 'reconstruct_versions_for_indexable', 'get_all_accesible_indicies' ] ) + /** @var MockObject&Versioning */ + $partially_mocked_versioning = $this->getMockBuilder( Versioning::class ) + ->onlyMethods( [ 'get_versions', 'reconstruct_versions_for_indexable', 'get_all_accesible_indicies' ] ) ->getMock(); $partially_mocked_versioning @@ -1745,7 +1745,7 @@ public function test__maybe_self_heal_reconstruct( $indexables, $versioning, $ex ->method( 'reconstruct_versions_for_indexable' ); $partially_mocked_versioning->elastic_search_indexables = $indexables_mock; - $partially_mocked_versioning->alerts = $this->createMock( \Automattic\VIP\Utils\Alerts::class ); + $partially_mocked_versioning->alerts = $this->createMock( Alerts::class ); $partially_mocked_versioning->maybe_self_heal(); } @@ -1788,18 +1788,16 @@ public function get_all_accesible_indicies_data() { * @dataProvider get_all_accesible_indicies_data */ public function test__get_all_accesible_indicies( $response, $expected ) { - $es_mock = $this->getMockBuilder( \ElasticPress\Elasticsearch::class ) - ->setMethods( [ 'remote_request' ] ) + $es_mock = $this->getMockBuilder( Elasticsearch::class ) + ->onlyMethods( [ 'remote_request' ] ) ->getMock(); $es_mock->expects( $this->once() ) ->method( 'remote_request' ) ->willReturn( $response ); - $instance = new Versioning(); $instance->elastic_search_instance = $es_mock; - $result = $instance->get_all_accesible_indicies(); $this->assertEquals( $expected, $result ); @@ -1926,7 +1924,7 @@ public function reconstruct_versions_for_indexable_data() { * @dataProvider reconstruct_versions_for_indexable_data */ public function test__reconstruct_versions_for_indexable( $indicies, $indexable_data, $expected ) { - $indexable_mock = $this->getMockBuilder( \ElasticPress\Indexable::class )->getMock(); + $indexable_mock = $this->getMockBuilder( Indexable::class )->getMock(); $indexable_mock->slug = $indexable_data['slug']; $indexable_mock->global = $indexable_data['global']; @@ -1964,7 +1962,7 @@ public function parse_index_name_data() { ], [ 'vip-200', - new \WP_Error( 'index-name-not-valid', 'Index name "vip-200" is not valid' ), + new WP_Error( 'index-name-not-valid', 'Index name "vip-200" is not valid' ), ], ]; @@ -1980,7 +1978,7 @@ public function test__parse_index_name( $index, $expected ) { } public function test__create_versioned_index_with_mapping__ok_mapping() { - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); add_filter( 'ep_do_intercept_request', [ $this, 'filter_put_mapping_request_ok' ], PHP_INT_MAX, 5 ); @@ -1992,7 +1990,7 @@ public function test__create_versioned_index_with_mapping__ok_mapping() { } public function test__create_versioned_index_with_mapping__bad_mapping() { - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $indexable = Indexables::factory()->get( 'post' ); add_filter( 'ep_do_intercept_request', [ $this, 'filter_put_mapping_request_bad' ], PHP_INT_MAX, 5 ); @@ -2063,16 +2061,16 @@ public function test__get_index_name__post( $version, $blog_id, $app_id, $expect Constant_Mocker::clear(); Constant_Mocker::define( 'FILES_CLIENT_SITE_ID', $app_id ); - /** @var \ElasticPress\Indexable&MockObject */ - $mocked_indexable = $this->getMockBuilder( \ElasticPress\Indexable::class ) - ->setMethods( self::$indexable_methods ) + /** @var Indexable&MockObject */ + $mocked_indexable = $this->getMockBuilder( Indexable::class ) + ->onlyMethods( self::$indexable_methods ) ->getMock(); if ( 'post' === $indexable ) { - /** @var \ElasticPress\Indexable\Post&MockObject $mocked_indexable */ + /** @var Indexable\Post&MockObject $mocked_indexable */ $mocked_indexable->slug = 'post'; $mocked_indexable->global = false; } elseif ( 'user' === $indexable ) { - /** @var \ElasticPress\Indexable\User&MockObject $mocked_indexable */ + /** @var Indexable\User&MockObject $mocked_indexable */ $mocked_indexable->slug = 'user'; $mocked_indexable->global = true; } diff --git a/tests/search/includes/classes/test-class-versioningcleanupjob.php b/tests/search/includes/classes/test-class-versioningcleanupjob.php index efedf126fd..759aa83da3 100644 --- a/tests/search/includes/classes/test-class-versioningcleanupjob.php +++ b/tests/search/includes/classes/test-class-versioningcleanupjob.php @@ -2,6 +2,9 @@ namespace Automattic\VIP\Search; +use ElasticPress\Indexable; +use ElasticPress\Indexables; +use PHPUnit\Framework\MockObject\MockObject; use WP_UnitTestCase; class VersioningCleanupJob_Test extends WP_UnitTestCase { @@ -13,24 +16,24 @@ public function setUp(): void { public function test__versioning_cleanup__will_check_for_all_indexables() { $indexables_mocks = array_map( function ( $slug ) { - $indexable_mock = $this->getMockBuilder( \ElasticPress\Indexable::class )->getMock(); + $indexable_mock = $this->getMockBuilder( Indexable::class )->getMock(); $indexable_mock->slug = $slug; return $indexable_mock; }, [ 'foo', 'bar' ] ); - $indexables_mock = $this->getMockBuilder( \ElasticPress\Indexables::class ) - ->setMethods( [ 'get_all' ] ) + $indexables_mock = $this->getMockBuilder( Indexables::class ) + ->onlyMethods( [ 'get_all' ] ) ->getMock(); $indexables_mock->method( 'get_all' )->willReturn( $indexables_mocks ); - $versioning_mock = $this->getMockBuilder( \Automattic\VIP\Search\Versioning::class ) - ->setMethods( [ 'delete_version' ] ) - ->getMock(); - + $versioning_mock = $this->getMockBuilder( Versioning::class ) + ->onlyMethods( [ 'delete_version' ] ) + ->getMock(); - $partially_mocked_instance = $this->getMockBuilder( \Automattic\VIP\Search\VersioningCleanupJob::class ) + /** @var MockObject&VersioningCleanupJob */ + $partially_mocked_instance = $this->getMockBuilder( VersioningCleanupJob::class ) ->disableOriginalConstructor() - ->setMethods( [ 'delete_stale_inactive_version', 'get_stale_inactive_versions' ] ) + ->onlyMethods( [ 'delete_stale_inactive_version', 'get_stale_inactive_versions' ] ) ->getMock(); $partially_mocked_instance @@ -40,14 +43,15 @@ public function test__versioning_cleanup__will_check_for_all_indexables() { $partially_mocked_instance->indexables = $indexables_mock; $partially_mocked_instance->versioning = $versioning_mock; + $call_index = 0; $partially_mocked_instance->expects( $this->exactly( 4 ) ) ->method( 'delete_stale_inactive_version' ) - ->withConsecutive( - [ $this->equalTo( $indexables_mocks[0] ), $this->equalTo( 1 ) ], - [ $this->equalTo( $indexables_mocks[0] ), $this->equalTo( 2 ) ], - [ $this->equalTo( $indexables_mocks[1] ), $this->equalTo( 1 ) ], - [ $this->equalTo( $indexables_mocks[1] ), $this->equalTo( 2 ) ] - ); + ->with( $this->callback( function ( $indexable ) use ( $indexables_mocks, &$call_index ) { + $expected_index = intdiv( $call_index, 2 ); + $expected_number = $call_index % 2 + 1; + ++$call_index; + return $indexable === $indexables_mocks[ $expected_index ] && $expected_number; + })); $partially_mocked_instance->versioning_cleanup(); } @@ -170,14 +174,15 @@ public function get_stale_inactive_versions_data() { public function test__get_stale_inactive_versions( $input_versions, $active_version, $expected_numbers ) { $versions = $input_versions; - $indexable_mock = $this->getMockBuilder( \ElasticPress\Indexable::class )->getMock(); + /** @var MockObject&Indexable */ + $indexable_mock = $this->getMockBuilder( Indexable::class )->getMock(); - $versioning_mock = $this->getMockBuilder( \Automattic\VIP\Search\Versioning::class ) - ->setMethods( [ 'get_versions', 'get_active_version' ] ) + $versioning_mock = $this->getMockBuilder( Versioning::class ) + ->onlyMethods( [ 'get_versions', 'get_active_version' ] ) ->getMock(); $versioning_mock->method( 'get_versions' )->willReturn( $versions ); $versioning_mock->method( 'get_active_version' )->willReturn( $active_version ); - $instance = new \Automattic\VIP\Search\VersioningCleanupJob( null, $versioning_mock ); + $instance = new VersioningCleanupJob( null, $versioning_mock ); $result = $instance->get_stale_inactive_versions( $indexable_mock ); diff --git a/tests/test-vip-integrations.php b/tests/test-vip-integrations.php index 311936ab01..d33f0d63e9 100644 --- a/tests/test-vip-integrations.php +++ b/tests/test-vip-integrations.php @@ -15,7 +15,7 @@ class VIP_Integrations_Plugin_Test extends WP_UnitTestCase { public function test_activate_function_is_calling_the_activate_method_from_integrations_class(): void { - $integrations_mock = $this->getMockBuilder( Integrations::class )->setMethods( [ 'activate' ] )->getMock(); + $integrations_mock = $this->getMockBuilder( Integrations::class )->onlyMethods( [ 'activate' ] )->getMock(); $integrations_mock->expects( $this->once() )->method( 'activate' )->with( $this->equalTo( 'test-slug' ), $this->equalTo( [ 'test-key' => 'test-value' ] ) ); $this->set_integrations( $integrations_mock ); @@ -46,7 +46,7 @@ public function test_activated_integrations_are_loaded_on_muplugins_loaded_hook( /** * Set integrations mock. * - * @param MockObject $mock + * @param MockObject&Integrations $mock */ private function set_integrations( $mock ): void { $instance = IntegrationsSingleton::instance(); diff --git a/tests/test-vip-mail.php b/tests/test-vip-mail.php index 8973e5d1d8..0489f1c5f4 100644 --- a/tests/test-vip-mail.php +++ b/tests/test-vip-mail.php @@ -17,10 +17,9 @@ public function setUp(): void { } public function tearDown(): void { - parent::tearDown(); - Constant_Mocker::clear(); - remove_all_filters( 'vip_block_wp_mail' ); + reset_phpmailer_instance(); + parent::tearDown(); } public function test__all_smtp_servers__not_array() { diff --git a/tests/vip-support/test-user.php b/tests/vip-support/test-user.php index d49815ad95..42d95b2066 100644 --- a/tests/vip-support/test-user.php +++ b/tests/vip-support/test-user.php @@ -20,6 +20,13 @@ public function setUp(): void { 'user_login' => 'vip-support', 'user_pass' => 'password', ) ); + + reset_phpmailer_instance(); + } + + public function tearDown(): void { + reset_phpmailer_instance(); + parent::tearDown(); } public function test_is_a8c_email(): void { diff --git a/vip-helpers/vip-caching.php b/vip-helpers/vip-caching.php index f0a8d2b5ae..308b6494aa 100644 --- a/vip-helpers/vip-caching.php +++ b/vip-helpers/vip-caching.php @@ -98,55 +98,6 @@ function wpcom_vip_get_term_link( $term, $taxonomy = null ) { return get_term_link( $term_object ); } -/** - * Cached version of get_page_by_title so that we're not making unnecessary SQL all the time - * - * @param string $page_title Page title - * @param string $output Optional. Output type; OBJECT*, ARRAY_N, or ARRAY_A. - * @param string $post_type Optional. Post type; default is 'page'. - * @return WP_Post|null WP_Post on success or null on failure - * @link https://docs.wpvip.com/technical-references/caching/uncached-functions/ Uncached Functions - */ -function wpcom_vip_get_page_by_title( $title, $output = OBJECT, $post_type = 'page' ) { - global $wp_version; - if ( version_compare( $wp_version, '6.2', '<' ) ) { - _deprecated_function( __FUNCTION__, '6.2', 'WP_Query' ); - } - - $cache_key = $post_type . '_' . sanitize_key( $title ); - $page_id = wp_cache_get( $cache_key, 'get_page_by_title' ); - - if ( false === $page_id ) { - if ( ! function_exists( 'is_user_logged_in' ) ) { - // If too early to call `WP_Query`, fallback to deprecated `get_page_by_title` - // phpcs:ignore WordPress.WP.DeprecatedFunctions.get_page_by_titleFound - $page = get_page_by_title( $title, OBJECT, $post_type ); - $page_id = $page ? $page->ID : 0; - } else { - // WP 6.2 deprecates `get_page_by_title` in favor of `WP_Query` - $query = new WP_Query( - array( - 'title' => $title, - 'post_type' => $post_type, - 'posts_per_page' => 1, - 'orderby' => 'ID', - 'order' => 'ASC', - 'no_found_rows' => true, - 'fields' => 'ids', - ), - ); - $page_id = ! empty( $query->posts ) ? $query->posts[0] : 0; - } - wp_cache_set( $cache_key, $page_id, 'get_page_by_title', 3 * HOUR_IN_SECONDS ); // We only store the ID to keep our footprint small - } - - if ( $page_id ) { - return get_post( $page_id, $output ); - } - - return null; -} - /** * Flush the cache for published pages so we don't end up with stale data * diff --git a/vip-helpers/vip-deprecated.php b/vip-helpers/vip-deprecated.php index 64f7c0c479..1331e0ce7d 100644 --- a/vip-helpers/vip-deprecated.php +++ b/vip-helpers/vip-deprecated.php @@ -1329,20 +1329,62 @@ function wpcom_vip_term_exists( $term, $taxonomy = '', $parent = null ) { return term_exists( $term, $taxonomy, $parent ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.term_exists_term_exists } -if ( ! function_exists( 'wpcom_vip_get_page_by_path' ) ) { +/** + * `get_page_by_path()` is now cached and no longer calls direct SQL. + * + * @deprecated Since WP 6.1 + * + * @param string $page_path Page path + * @param string $output Optional. Output type; OBJECT*, ARRAY_N, or ARRAY_A. + * @param string|array $post_type Optional. Post type; default is 'page'. + * @return WP_Post|null WP_Post on success or null on failure + */ +function wpcom_vip_get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) { + _deprecated_function( __FUNCTION__, '6.1', 'get_page_by_path' ); + + return get_page_by_path( $page_path, $output, $post_type ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.get_page_by_path_get_page_by_path +} + +if ( ! function_exists( 'wpcom_vip_get_page_by_title' ) ) { /** - * `get_page_by_path()` is now cached and no longer calls direct SQL. - * - * @deprecated Since WP 6.1 - * - * @param string $page_path Page path - * @param string $output Optional. Output type; OBJECT*, ARRAY_N, or ARRAY_A. - * @param string|array $post_type Optional. Post type; default is 'page'. + * Use `WP_Query` to get a page by title. + * + * @param string $page_title Page title + * @param string $output Optional. Output type; OBJECT*, ARRAY_N, or ARRAY_A. + * @param string $post_type Optional. Post type; default is 'page'. * @return WP_Post|null WP_Post on success or null on failure + * @link https://docs.wpvip.com/technical-references/caching/uncached-functions/ Uncached Functions */ - function wpcom_vip_get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) { - _deprecated_function( __FUNCTION__, '6.1', 'get_page_by_path' ); + function wpcom_vip_get_page_by_title( $title, $output = OBJECT, $post_type = 'page' ) { + global $wp_version; + if ( version_compare( $wp_version, '6.2', '<' ) ) { + _deprecated_function( __FUNCTION__, '6.2', 'WP_Query' ); + } + + $cache_key = $post_type . '_' . sanitize_key( $title ); + $page_id = wp_cache_get( $cache_key, 'get_page_by_title' ); + + if ( false === $page_id ) { + // WP 6.2 deprecates `get_page_by_title` in favor of `WP_Query` + $query = new WP_Query( + array( + 'title' => $title, + 'post_type' => $post_type, + 'posts_per_page' => 1, + 'orderby' => 'ID', + 'order' => 'ASC', + 'no_found_rows' => true, + 'fields' => 'ids', + ), + ); + $page_id = ! empty( $query->posts ) ? $query->posts[0] : 0; + wp_cache_set( $cache_key, $page_id, 'get_page_by_title', 3 * HOUR_IN_SECONDS ); // We only store the ID to keep our footprint small + } - return get_page_by_path( $page_path, $output, $post_type ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.get_page_by_path_get_page_by_path + if ( $page_id ) { + return get_post( $page_id, $output ); + } + + return null; } } diff --git a/vip-jetpack/connection-pilot/class-jetpack-connection-attendant.php b/vip-jetpack/connection-pilot/class-jetpack-connection-attendant.php new file mode 100644 index 0000000000..519f0413c0 --- /dev/null +++ b/vip-jetpack/connection-pilot/class-jetpack-connection-attendant.php @@ -0,0 +1,309 @@ +get_expected_owner_details(); + $legacy_owner_details = $this->get_legacy_owner_details(); + $org_owner_details = $this->get_org_owner_details(); + + $current_owner = $this->get_current_connection_owner(); + if ( ! isset( $current_owner->user_login ) ) { + return 'not_vip'; + } + + // We won't consider it the legacy user if it is still the expected owner. + if ( $expected_owner_details['login'] === $current_owner->user_login ) { + return 'is_vip'; + } + + if ( $legacy_owner_details && $legacy_owner_details['login'] === $current_owner->user_login ) { + return 'is_legacy_vip'; + } + + return 'not_vip'; + } + + /** + * Returns the user object of the connection owner. + * + * @return WP_User|null Null if no connection owner found. + */ + private function get_current_connection_owner() { + if ( ! method_exists( 'Jetpack', 'connection' ) ) { + return null; + } + + $jp_connection = \Jetpack::connection(); + + if ( ! method_exists( $jp_connection, 'get_connection_owner' ) ) { + return null; + } + + $connection_owner = $jp_connection->get_connection_owner(); + if ( false === $connection_owner ) { + return null; + } + + return $connection_owner; + } + + /** + * Ensures the VIP user exists on the site and has the necessary permissions. + * + * @return WP_User|WP_Error WP_User if successful, WP_Error otherwise. + */ + public function ensure_user_exists() { + $details = $this->get_expected_owner_details(); + + $user = get_user_by( 'login', $details['login'] ); + + // Create user if it doesn't exist yet. + if ( ! $user ) { + $result = wp_insert_user( [ + 'user_login' => $details['login'], + 'user_email' => $details['email'], + 'display_name' => $details['display_name'], + 'role' => $details['role'], + 'user_pass' => wp_generate_password( 100 ), + ] ); + + if ( is_wp_error( $result ) ) { + return new WP_Error( 'jp-cxn-pilot-owner-creation-failed', 'Failed to create new user.' ); + } + + $user = get_userdata( $result ); + if ( ! $user ) { + return new WP_Error( 'jp-cxn-pilot-owner-creation-failed', 'Failed to create new user.' ); + } + } + + // Ensure the email is correct & hasn't been changed. + if ( $details['email'] !== $user->user_email ) { + $result = wp_update_user( [ + 'ID' => $user->ID, + 'user_email' => $details['email'], + ] ); + + if ( is_wp_error( $result ) ) { + return new WP_Error( 'jp-cxn-pilot-owner-update-failed', 'Failed to correct the email address on the primary user.' ); + } + } + + // Add user to blog if needed, and ensure they are a super admin. + if ( is_multisite() ) { + $blog_id = get_current_blog_id(); + + if ( ! is_user_member_of_blog( $user->ID, $blog_id ) ) { + $added_to_blog = add_user_to_blog( $blog_id, $user->ID, $details['role'] ); + + if ( is_wp_error( $added_to_blog ) ) { + return new WP_Error( 'jp-cxn-pilot-owner-ms-add-failed', 'Failed to add user to blog. Error: ' . $added_to_blog->get_error_code() ); + } + } + + if ( ! is_super_admin( $user->ID ) ) { + // Returns false even if user already is SA. + grant_super_admin( $user->ID ); + } + } + + // Ensure the correct role is applied. + if ( ! in_array( $details['role'], (array) $user->roles, true ) ) { + $user->set_role( $details['role'] ); + } + + return $user; + } + + private function get_expected_owner_details() { + $legacy_owner_details = $this->get_legacy_owner_details(); + + // NOTE: This filter should be applied very early. Use before plugins_loaded priority 25 to be sure. + if ( null !== $legacy_owner_details && ! (bool) apply_filters( 'vip_jetpack_connection_pilot_use_org_owner', false ) ) { + return $legacy_owner_details; + } + + return $this->get_org_owner_details(); + } + + private function get_org_owner_details() { + $org_id = defined( 'VIP_ORG_ID' ) ? constant( 'VIP_ORG_ID' ) : 0; + + return [ + 'email' => "vip-jetpack-owner+{$org_id}@wpvip.com", + 'display_name' => 'WPVIP Jetpack Connection Owner', + 'login' => 'wpvip-jetpack-connection-owner', + 'role' => 'administrator', + ]; + } + + private function get_legacy_owner_details() { + $required_constants = [ + defined( 'WPCOM_VIP_MACHINE_USER_LOGIN' ), + defined( 'WPCOM_VIP_MACHINE_USER_ROLE' ), + defined( 'WPCOM_VIP_MACHINE_USER_NAME' ), + defined( 'WPCOM_VIP_MACHINE_USER_EMAIL' ), + ]; + + if ( in_array( false, $required_constants, true ) ) { + return null; + } + + return [ + 'email' => constant( 'WPCOM_VIP_MACHINE_USER_EMAIL' ), + 'display_name' => constant( 'WPCOM_VIP_MACHINE_USER_NAME' ), + 'login' => constant( 'WPCOM_VIP_MACHINE_USER_LOGIN' ), + 'role' => constant( 'WPCOM_VIP_MACHINE_USER_ROLE' ), + ]; + } + + /* + |-------------------------------------------------------------------------- + | Security Enhancements + |-------------------------------------------------------------------------- + */ + + /** + * Do not allow logging in to this owner account. + * Note that core runs authentication at priority 20. + * + * @param null|WP_User|WP_Error $user WP_User if the user is authenticated, WP_Error or null otherwise. + * @param string $username_or_email The username/email of the user attempting to authenticate. + * @return null|WP_User|WP_Error + */ + public function prevent_login( $user, $username_or_email ) { + $details = $this->get_org_owner_details(); + + if ( in_array( strtolower( $username_or_email ), [ $details['login'], $details['email'] ], true ) ) { + return new WP_Error( 'restricted-login', 'Logins are restricted for that user. Please try a different user account.' ); + } + + return $user; + } + + /** + * Disallow using application passwords for this owner account. + * + * @param bool $available True if available, false otherwise. + * @param WP_User $user The user to check. + * @return bool + */ + public function disable_application_passwords( $available, $user ) { + $details = $this->get_org_owner_details(); + + if ( $details['login'] === $user->user_login ) { + $available = false; + } + + return $available; + } + + /** + * Restrict modifications to the designated connection owner, and allow + * only this connection owner to manage the Jetpack connection's state. + * + * @param string[] $caps Primitive capabilities required of the user. + * @param string $cap Capability being checked. + * @param int $user_id The user ID. + * @param array $args Adds context to the capability check. + * + * @return string[] The capabilities required of the given user to satisfy the capability being checked. + */ + public function modify_user_capabilties( $caps, $requested_cap, $user_id, $args ) { + $caps_to_prevent = [ 'edit_user', 'delete_user', 'remove_user', 'promote_user' ]; + if ( in_array( $requested_cap, $caps_to_prevent, true ) ) { + $user_being_edited = get_userdata( $args[0] ); + + // Only the designated connection owner needs to be prevented from modifications. + $org_owner = $this->get_org_owner_details(); + if ( false !== $user_being_edited && $org_owner['login'] === $user_being_edited->user_login ) { + return [ 'do_not_allow' ]; + } + } + + $caps_to_restrict = [ 'jetpack_connect', 'jetpack_reconnect', 'jetpack_disconnect' ]; + $is_vip_env = defined( 'WPCOM_IS_VIP_ENV' ) && true === constant( 'WPCOM_IS_VIP_ENV' ); + if ( $is_vip_env && in_array( $requested_cap, $caps_to_restrict, true ) ) { + $user = get_userdata( $user_id ); + + // All users except the designated connection owners are restricted from managing connections. + $legacy_owner = $this->get_legacy_owner_details(); + $org_owner = $this->get_org_owner_details(); + if ( false !== $user && ! in_array( $user->user_login, [ $legacy_owner['login'] ?? 'wpcomvip', $org_owner['login'] ], true ) ) { + return [ 'do_not_allow' ]; + } + } + + return $caps; + } + + /** + * Bypass the VIP two-factor authentication requirement for the designated connection owner, + * otherwise Jetpack doesn't think the owner has any capabilities beyond subscriber. + * Note that logins for this account are already disabled. + * + * @return boolean + */ + public function bypass_two_factor_auth( $is_two_factor_forced ) { + $current_user = wp_get_current_user(); + + $org_owner = $this->get_org_owner_details(); + if ( $current_user->user_login === $org_owner['login'] ) { + $is_two_factor_forced = false; + } + + return $is_two_factor_forced; + } + + /** + * Tell Jetpack to not initialize the "My Jetpack" page when the connection owner is the bot user. + * + * @return void + */ + public function remove_my_jetpack_page() { + if ( ! is_admin() || wp_doing_ajax() ) { + return; + } + + if ( in_array( $this->check_connection_owner_validity(), [ 'is_vip', 'is_legacy_vip' ], true ) ) { + add_filter( 'jetpack_my_jetpack_should_initialize', '__return_false' ); + } + } +} diff --git a/vip-jetpack/connection-pilot/class-jetpack-connection-controls.php b/vip-jetpack/connection-pilot/class-jetpack-connection-controls.php index b7db9e6b48..a4b2915d77 100644 --- a/vip-jetpack/connection-pilot/class-jetpack-connection-controls.php +++ b/vip-jetpack/connection-pilot/class-jetpack-connection-controls.php @@ -17,8 +17,8 @@ class Controls { * @return mixed bool|WP_Error True if JP is properly connected, WP_Error otherwise. */ public static function jetpack_is_connected() { - if ( ! self::validate_constants() ) { - return new WP_Error( 'jp-cxn-pilot-missing-constants', 'This is not a valid VIP Go environment or some required constants are missing.' ); + if ( ! self::validate_environment() ) { + return new WP_Error( 'jp-cxn-pilot-invalid-environment', 'This is not a valid VIP Go environment.' ); } if ( ( new \Automattic\Jetpack\Status() )->is_offline_mode() ) { @@ -41,11 +41,11 @@ public static function jetpack_is_connected() { return new WP_Error( 'jp-cxn-pilot-not-active', 'Jetpack is not currently active.' ); } - $vip_machine_user = new \WP_User( \Jetpack_Options::get_option( 'master_user' ) ); - if ( ! $vip_machine_user->exists() ) { - return new WP_Error( 'jp-cxn-pilot-vip-user-missing', sprintf( 'The "%s" VIP user is missing.', WPCOM_VIP_MACHINE_USER_LOGIN ) ); - } elseif ( ! user_can( $vip_machine_user, 'manage_options' ) ) { - return new WP_Error( 'jp-cxn-pilot-vip-user-caps', sprintf( 'The "%s" VIP user does not have admin capabilities.', WPCOM_VIP_MACHINE_USER_LOGIN ) ); + $jp_primary_user = new \WP_User( \Jetpack_Options::get_option( 'master_user' ) ); + if ( ! $jp_primary_user->exists() ) { + return new WP_Error( 'jp-cxn-pilot-primary-user-missing', sprintf( 'Jetpack does not have a valid primary user.' ) ); + } elseif ( ! user_can( $jp_primary_user, 'manage_options' ) ) { + return new WP_Error( 'jp-cxn-pilot-primary-user-caps', sprintf( 'The Jetpack primary user does not have admin capabilities.' ) ); } $is_connected = self::test_jetpack_connection(); @@ -53,11 +53,12 @@ public static function jetpack_is_connected() { return $is_connected; } - $connection_owner = \Jetpack::connection()->get_connection_owner(); - $is_vip_connection = $connection_owner && WPCOM_VIP_MACHINE_USER_LOGIN === $connection_owner->user_login; - if ( ! $is_vip_connection ) { - $connection_owner_login = $connection_owner ? $connection_owner->user_login : 'unknown'; - return new WP_Error( 'jp-cxn-pilot-not-vip-owned', sprintf( 'The connection is not owned by "%s". Current connection owner is: "%s"', WPCOM_VIP_MACHINE_USER_LOGIN, $connection_owner_login ) ); + $attendant = Attendant::instance(); + switch ( $attendant->check_connection_owner_validity() ) { + case 'is_legacy_vip': + return new WP_Error( 'jp-cxn-pilot-has-legacy-vip-owner', 'The connection is owned by the legacy VIP user.' ); + case 'not_vip': + return new WP_Error( 'jp-cxn-pilot-not-vip-owned', 'The connection is not owned by VIP.' ); } $is_owner_connected = self::test_jetpack_owner_connection(); @@ -124,8 +125,8 @@ private static function test_jetpack_owner_connection(): bool { * @return mixed bool|WP_Error True if JP was (re)connected, WP_Error otherwise. */ public static function connect_site( bool $skip_connection_tests = false, bool $disconnect = false ) { - if ( ! self::validate_constants() ) { - return new WP_Error( 'jp-cxn-pilot-missing-constants', 'This is not a valid VIP Go environment or some constants are missing.' ); + if ( ! self::validate_environment() ) { + return new WP_Error( 'jp-cxn-pilot-invalid-environment', 'This is not a valid WPVIP environment.' ); } if ( ! $skip_connection_tests && ! $disconnect ) { @@ -141,7 +142,9 @@ public static function connect_site( bool $skip_connection_tests = false, bool $ \Jetpack::disconnect(); } - $user = self::maybe_create_user(); + + $attendant = Attendant::instance(); + $user = $attendant->ensure_user_exists(); if ( is_wp_error( $user ) ) { return $user; } @@ -180,7 +183,8 @@ public static function connect_akismet() { return true; } - $user = self::maybe_create_user(); + $attendant = Attendant::instance(); + $user = $attendant->ensure_user_exists(); if ( is_wp_error( $user ) ) { return $user; } @@ -244,60 +248,6 @@ public static function provision_site( $user_id ) { return new WP_Error( 'jp-cxn-pilot-provisioning-error', 'Unable to use Provisioner.' ); } - /** - * Maybe add our machine user to the site. Also sanity checks the user's permissions. - * - * @return object \WP_User if successful, WP_Error otherwise. - */ - private static function maybe_create_user() { - $user = get_user_by( 'login', WPCOM_VIP_MACHINE_USER_LOGIN ); - - if ( ! $user ) { - $user_id = wp_insert_user( array( - 'user_login' => WPCOM_VIP_MACHINE_USER_LOGIN, - 'user_email' => WPCOM_VIP_MACHINE_USER_EMAIL, - 'display_name' => WPCOM_VIP_MACHINE_USER_NAME, - 'role' => WPCOM_VIP_MACHINE_USER_ROLE, - 'user_pass' => wp_generate_password( 36 ), // This account can't be logged into, but just in case. - ) ); - - $user = get_userdata( $user_id ); - if ( is_wp_error( $user_id ) || ! $user ) { - return new WP_Error( 'jp-cxn-pilot-user-create-failed', 'Failed to create new user.' ); - } - } - - $user_id = $user->ID; - - // Add user to blog if needed, and ensure they are a super admin. - if ( is_multisite() ) { - $blog_id = get_current_blog_id(); - - if ( ! is_user_member_of_blog( $user_id, $blog_id ) ) { - $added_to_blog = add_user_to_blog( $blog_id, $user_id, WPCOM_VIP_MACHINE_USER_ROLE ); - - if ( is_wp_error( $added_to_blog ) ) { - return new WP_Error( 'jp-cxn-pilot-user-ms-create-failed', 'Failed to add user to blog.' ); - } - } - - if ( ! is_super_admin( $user_id ) ) { - // Will also return false if user already is SA. - grant_super_admin( $user_id ); - } - - return $user; - } - - // Ensure the correct role is applied. - $user_roles = (array) $user->roles; - if ( ! in_array( WPCOM_VIP_MACHINE_USER_ROLE, $user_roles, true ) ) { - $user->set_role( WPCOM_VIP_MACHINE_USER_ROLE ); - } - - return $user; - } - /** * Refresh the options cache. * @@ -319,19 +269,11 @@ private static function refresh_options_cache() { } /** - * Ensures we have all the needed constants available. + * Ensure we are in the right environment. * - * @return bool True if we have all the needed constants. + * @return bool */ - private static function validate_constants() { - $required_constants = [ - defined( 'WPCOM_VIP_MACHINE_USER_LOGIN' ), - defined( 'WPCOM_VIP_MACHINE_USER_ROLE' ), - defined( 'WPCOM_VIP_MACHINE_USER_NAME' ), - defined( 'WPCOM_VIP_MACHINE_USER_EMAIL' ), - defined( 'VIP_GO_APP_ID' ), - ]; - - return ! in_array( false, $required_constants, true ); + private static function validate_environment() { + return defined( 'WPCOM_IS_VIP_ENV' ) && true === constant( 'WPCOM_IS_VIP_ENV' ); } } diff --git a/vip-jetpack/connection-pilot/class-jetpack-connection-pilot.php b/vip-jetpack/connection-pilot/class-jetpack-connection-pilot.php index 1da7fc04a1..96cdf6bc3d 100644 --- a/vip-jetpack/connection-pilot/class-jetpack-connection-pilot.php +++ b/vip-jetpack/connection-pilot/class-jetpack-connection-pilot.php @@ -8,6 +8,10 @@ require_once __DIR__ . '/class-jetpack-connection-controls.php'; +if ( file_exists( __DIR__ . '/class-jetpack-connection-attendant.php' ) ) { + require_once __DIR__ . '/class-jetpack-connection-attendant.php'; +} + if ( defined( 'WP_CLI' ) && \WP_CLI ) { require_once __DIR__ . '/class-jetpack-connection-cli.php'; } @@ -58,9 +62,16 @@ private function __construct() { // The hook always needs to be available so the job can remove itself if it needs to. add_action( self::CRON_ACTION, array( '\Automattic\VIP\Jetpack\Connection_Pilot', 'do_cron' ) ); - if ( self::should_run_connection_pilot() ) { - $this->init_actions(); + // Always initiate so that bot user protections remain in place. + if ( class_exists( 'Automattic\VIP\Jetpack\Connection_Pilot\Attendant' ) ) { + Connection_Pilot\Attendant::instance(); } + + add_action( 'init', function () { + if ( self::should_run_connection_pilot() ) { + $this->init_actions(); + } + }, 25 ); } /** @@ -283,7 +294,7 @@ private function should_attempt_reconnection( \WP_Error $error = null ): bool { // 1) Handle specific errors where we don't want reconnection attempts. if ( is_wp_error( $error ) ) { switch ( $error->get_error_code() ) { - case 'jp-cxn-pilot-missing-constants': + case 'jp-cxn-pilot-invalid-environment': case 'jp-cxn-pilot-development-mode': $this->send_alert( 'Jetpack cannot currently be connected on this site due to the environment. JP may be in development mode.', $error ); return false; @@ -373,6 +384,6 @@ protected function send_alert( $message = '', $wp_error = null ) { } } -add_action( 'init', function () { +add_action( 'plugins_loaded', function () { Connection_Pilot::instance(); }, 25 ); diff --git a/vip-jetpack/vip-jetpack.php b/vip-jetpack/vip-jetpack.php index bf02ce0d24..8c9e737816 100644 --- a/vip-jetpack/vip-jetpack.php +++ b/vip-jetpack/vip-jetpack.php @@ -72,25 +72,6 @@ return $modules; }, 999 ); -/** - * Do not initialize my jetpack admin page for VIP Machine User - */ -add_action( 'plugins_loaded', function () { - if ( ! is_admin() || wp_doing_ajax() || ! method_exists( 'Jetpack', 'connection' ) || ! defined( 'WPCOM_VIP_MACHINE_USER_LOGIN' ) ) { - return; - } - - $jp_connection = Jetpack::connection(); - if ( method_exists( $jp_connection, 'get_connection_owner' ) ) { - $connection_owner = $jp_connection->get_connection_owner(); - $is_vip_connection = isset( $connection_owner->user_login ) && WPCOM_VIP_MACHINE_USER_LOGIN === $connection_owner->user_login; - - if ( $is_vip_connection ) { - add_filter( 'jetpack_my_jetpack_should_initialize', '__return_false' ); - } - } -} ); - /** * Lock down the jetpack_sync_settings_max_queue_size to an allowed range * @@ -159,24 +140,6 @@ }, 0, 2 ); // No need to wait till priority 10 since we're going to die anyway } -// On production servers, only our machine user can manage the Jetpack connection -if ( true === WPCOM_IS_VIP_ENV && is_admin() ) { - add_filter( 'map_meta_cap', function ( $caps, $cap, $user_id ) { - switch ( $cap ) { - case 'jetpack_connect': - case 'jetpack_reconnect': - case 'jetpack_disconnect': - $user = get_userdata( $user_id ); - if ( $user && WPCOM_VIP_MACHINE_USER_LOGIN !== $user->user_login ) { - return [ 'do_not_allow' ]; - } - break; - } - - return $caps; - }, 10, 3 ); -} - function wpcom_vip_did_jetpack_search_query( $query ) { if ( ! defined( 'SAVEQUERIES' ) || ! SAVEQUERIES ) { return; diff --git a/wp-parsely b/wp-parsely index 3e4cfe6c42..81a3160dca 160000 --- a/wp-parsely +++ b/wp-parsely @@ -1 +1 @@ -Subproject commit 3e4cfe6c42b5f1e8cd080ab042fc48077155b11b +Subproject commit 81a3160dca274a7f88bdce44a1a1fa4025691e9b