Commit ff645e3c50 for woocommerce
commit ff645e3c5000e2c28e14d73cb5d8c30ac926b609
Author: Hannah Tinkler <hannah.tinkler@gmail.com>
Date: Mon Jan 5 15:52:42 2026 +0000
Adds remaining CRUD methods to data store (#62024)
* Adds PushTokenDataStore for push token CRUD operations.
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index b4b852827a..a01212f6db 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -74509,83 +74509,17 @@ parameters:
path: src/Internal/ProductImage/MatchImageBySKU.php
-
- message: '#^Method Automattic\\WooCommerce\\Internal\\PushNotifications\\DataStores\\PushTokensDataStore\:\:create\(\) has no return type specified\.$#'
- identifier: missingType.return
- count: 1
- path: src/Internal/PushNotifications/DataStores/PushTokensDataStore.php
-
- -
- message: '#^Method Automattic\\WooCommerce\\Internal\\PushNotifications\\DataStores\\PushTokensDataStore\:\:read\(\) has no return type specified\.$#'
- identifier: missingType.return
- count: 1
- path: src/Internal/PushNotifications/DataStores/PushTokensDataStore.php
-
- -
- message: '#^Method Automattic\\WooCommerce\\Internal\\PushNotifications\\DataStores\\PushTokensDataStore\:\:update\(\) has no return type specified\.$#'
- identifier: missingType.return
- count: 1
- path: src/Internal/PushNotifications/DataStores/PushTokensDataStore.php
-
- -
- message: '#^Parameter \#1 \$post_id of function delete_post_meta expects int, int\|null given\.$#'
- identifier: argument.type
- count: 1
- path: src/Internal/PushNotifications/DataStores/PushTokensDataStore.php
-
- -
- message: '#^Parameter \#1 \$post_id of function get_post_meta expects int, int\|null given\.$#'
- identifier: argument.type
- count: 1
- path: src/Internal/PushNotifications/DataStores/PushTokensDataStore.php
-
- -
- message: '#^Parameter \#1 \$postarr of function wp_insert_post expects array\{ID\?\: int, post_author\?\: int, post_date\?\: string, post_date_gmt\?\: string, post_content\?\: string, post_content_filtered\?\: string, post_title\?\: string, post_excerpt\?\: string, \.\.\.\}, array\{post_author\: int\|null, post_type\: ''push_token'', post_status\: ''private'', meta_input\: array\{platform\: string\|null, token\: string\|null, device_uuid\?\: string, origin\: string\|null\}\} given\.$#'
+ message: '#^Parameter \#1 \$id of method Automattic\\WooCommerce\\Internal\\PushNotifications\\Entities\\PushToken\:\:set_id\(\) expects int, int\|WP_Post given\.$#'
identifier: argument.type
- count: 1
+ count: 2
path: src/Internal/PushNotifications/DataStores/PushTokensDataStore.php
-
- message: '#^Parameter \#1 \$postarr of function wp_update_post expects array\{ID\?\: int, post_author\?\: int, post_date\?\: string, post_date_gmt\?\: string, post_content\?\: string, post_content_filtered\?\: string, post_title\?\: string, post_excerpt\?\: string, \.\.\.\}, array\{ID\: int\|null, post_author\: int\|null, post_type\: ''push_token'', post_status\: ''private'', meta_input\: array\{platform\: string\|null, token\: string\|null, device_uuid\?\: string, origin\: string\|null\}\} given\.$#'
+ message: '#^Parameter \#2 \$object_ids of function update_meta_cache expects array\<int\>\|string, array\<int\|WP_Post\> given\.$#'
identifier: argument.type
count: 1
path: src/Internal/PushNotifications/DataStores/PushTokensDataStore.php
- -
- message: '#^Method Automattic\\WooCommerce\\Internal\\PushNotifications\\Entities\\PushToken\:\:set_device_uuid\(\) has no return type specified\.$#'
- identifier: missingType.return
- count: 1
- path: src/Internal/PushNotifications/Entities/PushToken.php
-
- -
- message: '#^Method Automattic\\WooCommerce\\Internal\\PushNotifications\\Entities\\PushToken\:\:set_id\(\) has no return type specified\.$#'
- identifier: missingType.return
- count: 1
- path: src/Internal/PushNotifications/Entities/PushToken.php
-
- -
- message: '#^Method Automattic\\WooCommerce\\Internal\\PushNotifications\\Entities\\PushToken\:\:set_origin\(\) has no return type specified\.$#'
- identifier: missingType.return
- count: 1
- path: src/Internal/PushNotifications/Entities/PushToken.php
-
- -
- message: '#^Method Automattic\\WooCommerce\\Internal\\PushNotifications\\Entities\\PushToken\:\:set_platform\(\) has no return type specified\.$#'
- identifier: missingType.return
- count: 1
- path: src/Internal/PushNotifications/Entities/PushToken.php
-
- -
- message: '#^Method Automattic\\WooCommerce\\Internal\\PushNotifications\\Entities\\PushToken\:\:set_token\(\) has no return type specified\.$#'
- identifier: missingType.return
- count: 1
- path: src/Internal/PushNotifications/Entities/PushToken.php
-
- -
- message: '#^Method Automattic\\WooCommerce\\Internal\\PushNotifications\\Entities\\PushToken\:\:set_user_id\(\) has no return type specified\.$#'
- identifier: missingType.return
- count: 1
- path: src/Internal/PushNotifications/Entities/PushToken.php
-
-
message: '#^@param string \$css does not accept actual type of parameter\: string\|false\.$#'
identifier: parameter.phpDocType
diff --git a/plugins/woocommerce/src/Internal/Features/FeaturesController.php b/plugins/woocommerce/src/Internal/Features/FeaturesController.php
index 4634939e8f..98083025da 100644
--- a/plugins/woocommerce/src/Internal/Features/FeaturesController.php
+++ b/plugins/woocommerce/src/Internal/Features/FeaturesController.php
@@ -556,7 +556,7 @@ class FeaturesController {
'enabled_by_default' => false,
'is_experimental' => true,
'disable_ui' => true,
- 'skip_compatibility_checks' => true,
+ 'skip_compatibility_checks' => false,
'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
),
'rest_api_caching' => array(
diff --git a/plugins/woocommerce/src/Internal/PushNotifications/DataStores/PushTokensDataStore.php b/plugins/woocommerce/src/Internal/PushNotifications/DataStores/PushTokensDataStore.php
index 940097f3eb..40f40f51cb 100644
--- a/plugins/woocommerce/src/Internal/PushNotifications/DataStores/PushTokensDataStore.php
+++ b/plugins/woocommerce/src/Internal/PushNotifications/DataStores/PushTokensDataStore.php
@@ -10,9 +10,10 @@ namespace Automattic\WooCommerce\Internal\PushNotifications\DataStores;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Internal\PushNotifications\Entities\PushToken;
+use Automattic\WooCommerce\Internal\PushNotifications\Exceptions\PushTokenNotFoundException;
use Exception;
use InvalidArgumentException;
-use WP_Http;
+use WP_Query;
/**
* Data store class for push tokens.
@@ -20,6 +21,13 @@ use WP_Http;
* @since 10.5.0
*/
class PushTokensDataStore {
+ const SUPPORTED_META = array(
+ 'origin',
+ 'device_uuid',
+ 'token',
+ 'platform',
+ );
+
/**
* Creates a post representing the push token.
*
@@ -27,38 +35,28 @@ class PushTokensDataStore {
* @param PushToken $push_token An instance of PushToken.
* @throws InvalidArgumentException If the token can't be created.
* @throws Exception If the token creation fails.
+ * @return void
*/
- public function create( &$push_token ) {
+ public function create( PushToken &$push_token ): void {
if ( ! $push_token->can_be_created() ) {
throw new InvalidArgumentException(
- 'Can\'t create push token because the push token data provided is invalid.',
- // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
- WP_Http::BAD_REQUEST
+ 'Can\'t create push token because the push token data provided is invalid.'
);
}
$id = wp_insert_post(
array(
- 'post_author' => $push_token->get_user_id(),
+ 'post_author' => (int) $push_token->get_user_id(),
'post_type' => PushToken::POST_TYPE,
'post_status' => 'private',
- 'meta_input' => array_filter(
- array(
- 'platform' => $push_token->get_platform(),
- 'token' => $push_token->get_token(),
- 'device_uuid' => $push_token->get_device_uuid(),
- 'origin' => $push_token->get_origin(),
- ),
- static fn ( $value, $key ) => 'device_uuid' !== $key || null !== $value,
- ARRAY_FILTER_USE_BOTH
- ),
+ 'meta_input' => $this->build_meta_array_from_token( $push_token ),
),
true
);
if ( is_wp_error( $id ) ) {
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
- throw new Exception( $id->get_error_message(), WP_Http::INTERNAL_SERVER_ERROR );
+ throw new Exception( $id->get_error_message() );
}
$push_token->set_id( $id );
@@ -70,26 +68,23 @@ class PushTokensDataStore {
* @since 10.5.0
* @param PushToken $push_token An instance of PushToken.
* @throws InvalidArgumentException If the token can't be read.
- * @throws Exception If the token can't be found.
- * @throws Exception If the ID doesn't belong to a push token.
+ * @throws PushTokenNotFoundException If the token can't be found.
+ * @return void
*/
- public function read( &$push_token ) {
+ public function read( PushToken &$push_token ): void {
if ( ! $push_token->can_be_read() ) {
throw new InvalidArgumentException(
- 'Can\'t read push token because the push token data provided is invalid.',
- // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
- WP_Http::BAD_REQUEST
+ 'Can\'t read push token because the push token data provided is invalid.'
);
}
$post = get_post( $push_token->get_id() );
if ( ! $post || PushToken::POST_TYPE !== $post->post_type ) {
- // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
- throw new Exception( 'Push token could not be found.', WP_Http::NOT_FOUND );
+ throw new PushTokenNotFoundException( 'Push token could not be found.' );
}
- $meta = $this->read_meta( $push_token );
+ $meta = $this->build_meta_array_from_database( $push_token );
if (
empty( $meta['token'] )
@@ -101,9 +96,7 @@ class PushTokensDataStore {
)
) {
throw new InvalidArgumentException(
- 'Can\'t read push token because the push token record is malformed.',
- // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
- WP_Http::BAD_REQUEST
+ 'Can\'t read push token because the push token record is malformed.'
);
}
@@ -120,14 +113,14 @@ class PushTokensDataStore {
* @since 10.5.0
* @param PushToken $push_token An instance of PushToken.
* @throws InvalidArgumentException If the token can't be updated.
+ * @throws PushTokenNotFoundException If the token can't be found.
* @throws Exception If the token update fails.
+ * @return void
*/
- public function update( &$push_token ) {
+ public function update( PushToken &$push_token ): void {
if ( ! $push_token->can_be_updated() ) {
throw new InvalidArgumentException(
- 'Can\'t update push token because the push token data provided is invalid.',
- // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
- WP_Http::BAD_REQUEST
+ 'Can\'t update push token because the push token data provided is invalid.'
);
}
@@ -135,59 +128,198 @@ class PushTokensDataStore {
if ( ! $post || PushToken::POST_TYPE !== $post->post_type ) {
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
- throw new Exception( 'Push token could not be found.', WP_Http::NOT_FOUND );
+ throw new PushTokenNotFoundException( 'Push token could not be found.' );
}
$result = wp_update_post(
array(
- 'ID' => $push_token->get_id(),
- 'post_author' => $push_token->get_user_id(),
+ 'ID' => (int) $push_token->get_id(),
+ 'post_author' => (int) $push_token->get_user_id(),
'post_type' => PushToken::POST_TYPE,
'post_status' => 'private',
- 'meta_input' => array_filter(
- array(
- 'platform' => $push_token->get_platform(),
- 'token' => $push_token->get_token(),
- 'device_uuid' => $push_token->get_device_uuid(),
- 'origin' => $push_token->get_origin(),
- ),
- static fn ( $value, $key ) => 'device_uuid' !== $key || null !== $value,
- ARRAY_FILTER_USE_BOTH
- ),
+ 'meta_input' => $this->build_meta_array_from_token( $push_token ),
),
true
);
if ( is_wp_error( $result ) ) {
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
- throw new Exception( $result->get_error_message(), WP_Http::INTERNAL_SERVER_ERROR );
+ throw new Exception( $result->get_error_message() );
}
if ( null === $push_token->get_device_uuid() ) {
- delete_post_meta( $push_token->get_id(), 'device_uuid' );
+ delete_post_meta( (int) $push_token->get_id(), 'device_uuid' );
}
}
/**
- * Returns an array of post meta objects as key => value pairs.
+ * Deletes a push token.
+ *
+ * @since 10.5.0
+ * @param PushToken $push_token An instance of PushToken.
+ * @throws InvalidArgumentException If the token can't be deleted.
+ * @throws PushTokenNotFoundException If the token can't be found.
+ * @return void
+ */
+ public function delete( PushToken &$push_token ): void {
+ if ( ! $push_token->can_be_deleted() ) {
+ throw new InvalidArgumentException(
+ 'Can\'t delete push token because the push token data provided is invalid.'
+ );
+ }
+
+ $post = get_post( $push_token->get_id() );
+
+ if ( ! $post || PushToken::POST_TYPE !== $post->post_type ) {
+ throw new PushTokenNotFoundException( 'Push token could not be found.' );
+ }
+
+ wp_delete_post( (int) $push_token->get_id(), true );
+ }
+
+ /**
+ * Find tokens for this user and platform that match either the token
+ * or device UUID. We check the token value to avoid creating a duplicate.
+ * We check the device UUID value because only one token should be issued
+ * per device, therefore if we already have one then we can update it to
+ * avoid creating a duplicate.
+ *
+ * @since 10.5.0
+ * @param PushToken $push_token An instance of PushToken.
+ * @return null|PushToken
+ * @throws InvalidArgumentException If push token is missing data.
+ */
+ public function get_by_token_or_device_id( PushToken &$push_token ): ?PushToken {
+ if (
+ ! $push_token->get_user_id()
+ || ! $push_token->get_platform()
+ || ! $push_token->get_origin()
+ || (
+ /**
+ * Platforms iOS and Android require token OR device UUID.
+ */
+ $push_token->get_platform() !== PushToken::PLATFORM_BROWSER
+ && ! $push_token->get_token()
+ && ! $push_token->get_device_uuid()
+ )
+ || (
+ /**
+ * Browsers don't have device UUIDs, so require token.
+ */
+ $push_token->get_platform() === PushToken::PLATFORM_BROWSER
+ && ! $push_token->get_token()
+ )
+ ) {
+ throw new InvalidArgumentException(
+ 'Can\'t retrieve push token because the push token data provided is invalid.'
+ );
+ }
+
+ $query = new WP_Query(
+ array(
+ 'post_type' => PushToken::POST_TYPE,
+ 'post_status' => 'private',
+ 'author' => $push_token->get_user_id(),
+ 'posts_per_page' => -1,
+ 'orderby' => 'ID',
+ 'order' => 'DESC',
+ 'fields' => 'ids',
+ )
+ );
+
+ $post_ids = $query->posts;
+
+ if ( empty( $post_ids ) ) {
+ return null;
+ }
+
+ update_meta_cache( 'post', $post_ids );
+
+ foreach ( $post_ids as $post_id ) {
+ $candidate = new PushToken();
+ $candidate->set_id( $post_id );
+
+ try {
+ $meta = $this->build_meta_array_from_database( $candidate );
+ } catch ( Exception $e ) {
+ wc_get_logger()->warning(
+ 'Failed to load meta for push token.',
+ array(
+ 'token_id' => $post_id,
+ 'error' => $e->getMessage(),
+ )
+ );
+
+ continue;
+ }
+
+ if (
+ $meta['platform'] === $push_token->get_platform()
+ && $meta['origin'] === $push_token->get_origin()
+ && (
+ ( $push_token->get_token() && $push_token->get_token() === $meta['token'] )
+ || ( $push_token->get_device_uuid() && $push_token->get_device_uuid() === $meta['device_uuid'] )
+ )
+ ) {
+ $push_token->set_id( $post_id );
+ $push_token->set_token( $meta['token'] );
+ $push_token->set_device_uuid( $meta['device_uuid'] );
+ return $push_token;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns an associative array of post meta as key => value pairs for the
+ * keys defined in SUPPORTED_META; missing keys return null.
*
* @since 10.5.0
* @param PushToken $push_token An instance of PushToken.
* @return array
* @throws InvalidArgumentException If the token can't be read.
*/
- public function read_meta( &$push_token ) {
+ private function build_meta_array_from_database( PushToken &$push_token ) {
if ( ! $push_token->can_be_read() ) {
throw new InvalidArgumentException(
- 'Can\'t read meta for push token because the push token data provided is invalid.',
- // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
- WP_Http::BAD_REQUEST
+ 'Can\'t read meta for push token because the push token data provided is invalid.'
);
}
- return array_map(
- static fn ( $meta ) => $meta[0] ?? $meta,
- get_post_meta( $push_token->get_id() )
+ $meta = (array) get_post_meta( (int) $push_token->get_id() );
+ $meta_by_key = (array) array_combine( static::SUPPORTED_META, static::SUPPORTED_META );
+
+ foreach ( static::SUPPORTED_META as $key ) {
+ if ( ! isset( $meta[ $key ] ) ) {
+ $meta_by_key[ $key ] = null;
+ } elseif ( is_array( $meta[ $key ] ) ) {
+ $meta_by_key[ $key ] = $meta[ $key ][0];
+ } else {
+ $meta_by_key[ $key ] = $meta[ $key ];
+ }
+ }
+
+ return $meta_by_key;
+ }
+
+ /**
+ * Returns an associative array of post meta as key => value pairs, built
+ * using push token properties.
+ *
+ * @since 10.5.0
+ * @param PushToken $push_token An instance of PushToken.
+ * @return array
+ * @throws InvalidArgumentException If the token can't be read.
+ */
+ private function build_meta_array_from_token( PushToken &$push_token ) {
+ return array_filter(
+ array(
+ 'platform' => $push_token->get_platform(),
+ 'token' => $push_token->get_token(),
+ 'device_uuid' => $push_token->get_device_uuid(),
+ 'origin' => $push_token->get_origin(),
+ )
);
}
}
diff --git a/plugins/woocommerce/src/Internal/PushNotifications/Entities/PushToken.php b/plugins/woocommerce/src/Internal/PushNotifications/Entities/PushToken.php
index 5abb951e54..a1676b4e8c 100644
--- a/plugins/woocommerce/src/Internal/PushNotifications/Entities/PushToken.php
+++ b/plugins/woocommerce/src/Internal/PushNotifications/Entities/PushToken.php
@@ -17,12 +17,12 @@ class PushToken {
/**
* WordPress post type for storing push tokens.
*/
- const POST_TYPE = 'push_token';
+ const POST_TYPE = 'wc_push_token';
/**
- * Platform identifier for iOS devices.
+ * Platform identifier for Apple devices.
*/
- const PLATFORM_IOS = 'ios';
+ const PLATFORM_APPLE = 'apple';
/**
* Platform identifier for Android devices.
@@ -63,7 +63,7 @@ class PushToken {
* List of valid platforms.
*/
const PLATFORMS = array(
- self::PLATFORM_IOS,
+ self::PLATFORM_APPLE,
self::PLATFORM_ANDROID,
self::PLATFORM_BROWSER,
);
@@ -188,10 +188,11 @@ class PushToken {
*
* @param int $id The id of the token post.
* @throws InvalidArgumentException If ID is <= 0.
+ * @return void
*
* @since 10.4.0
*/
- public function set_id( int $id ) {
+ public function set_id( int $id ): void {
if ( $id <= 0 ) {
throw new InvalidArgumentException( 'ID must be a positive integer.' );
}
@@ -204,10 +205,11 @@ class PushToken {
*
* @param int $user_id The id of the user who owns the token.
* @throws InvalidArgumentException If ID is <= 0.
+ * @return void
*
* @since 10.4.0
*/
- public function set_user_id( int $user_id ) {
+ public function set_user_id( int $user_id ): void {
if ( $user_id <= 0 ) {
throw new InvalidArgumentException( 'User ID must be a positive integer.' );
}
@@ -220,10 +222,11 @@ class PushToken {
*
* @param string $token The token representing a device we can send a push notification to.
* @throws InvalidArgumentException If token is empty or exceeds maximum length.
+ * @return void
*
* @since 10.4.0
*/
- public function set_token( string $token ) {
+ public function set_token( string $token ): void {
$token = trim( $token );
if ( '' === $token ) {
@@ -244,10 +247,11 @@ class PushToken {
* Sets the device UUID, normalize empty (non-null) values to null.
*
* @param string|null $device_uuid The UUID of the device that generated the token.
+ * @return void
*
* @since 10.4.0
*/
- public function set_device_uuid( ?string $device_uuid ) {
+ public function set_device_uuid( ?string $device_uuid ): void {
if ( null !== $device_uuid ) {
$device_uuid = trim( $device_uuid );
}
@@ -260,10 +264,11 @@ class PushToken {
*
* @param string $platform The platform the token was generated by.
* @throws InvalidArgumentException If the platform is invalid.
+ * @return void
*
* @since 10.4.0
*/
- public function set_platform( string $platform ) {
+ public function set_platform( string $platform ): void {
if ( ! in_array( $platform, self::PLATFORMS, true ) ) {
throw new InvalidArgumentException( 'Platform for PushToken is invalid.' );
}
@@ -276,10 +281,11 @@ class PushToken {
*
* @param string $origin The origin of the token, e.g. the app it came from.
* @throws InvalidArgumentException If the origin is invalid.
+ * @return void
*
* @since 10.4.0
*/
- public function set_origin( string $origin ) {
+ public function set_origin( string $origin ): void {
if ( ! in_array( $origin, self::ORIGINS, true ) ) {
throw new InvalidArgumentException( 'Origin for PushToken is invalid.' );
}
diff --git a/plugins/woocommerce/src/Internal/PushNotifications/Exceptions/PushTokenNotFoundException.php b/plugins/woocommerce/src/Internal/PushNotifications/Exceptions/PushTokenNotFoundException.php
new file mode 100644
index 0000000000..08e0ea9f16
--- /dev/null
+++ b/plugins/woocommerce/src/Internal/PushNotifications/Exceptions/PushTokenNotFoundException.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * PushTokenNotFoundException class file.
+ */
+
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Internal\PushNotifications\Exceptions;
+
+defined( 'ABSPATH' ) || exit;
+
+use Exception;
+use WP_Http;
+
+/**
+ * Exception thrown when a push token cannot be found.
+ *
+ * @since 10.5.0
+ */
+class PushTokenNotFoundException extends Exception {}
diff --git a/plugins/woocommerce/src/Internal/PushNotifications/PushNotifications.php b/plugins/woocommerce/src/Internal/PushNotifications/PushNotifications.php
index f254e29934..d89b87fa80 100644
--- a/plugins/woocommerce/src/Internal/PushNotifications/PushNotifications.php
+++ b/plugins/woocommerce/src/Internal/PushNotifications/PushNotifications.php
@@ -7,8 +7,10 @@ namespace Automattic\WooCommerce\Internal\PushNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\Jetpack\Connection\Manager as JetpackConnectionManager;
+use Automattic\WooCommerce\Internal\PushNotifications\Entities\PushToken;
use Automattic\WooCommerce\Proxies\LegacyProxy;
use Automattic\WooCommerce\Utilities\FeaturesUtil;
+use WC_Logger;
use Exception;
/**
@@ -53,9 +55,41 @@ class PushNotifications {
return;
}
+ add_action( 'init', array( $this, 'register_post_types' ) );
+
// Library endpoints and scheduled tasks will be registered here.
}
+ /**
+ * Registers the push token custom post type.
+ *
+ * @since 10.5.0
+ * @return void
+ */
+ public function register_post_types(): void {
+ register_post_type(
+ PushToken::POST_TYPE,
+ array(
+ 'labels' => array(
+ 'name' => __( 'Push Tokens', 'woocommerce' ),
+ 'singular_name' => __( 'Push Token', 'woocommerce' ),
+ ),
+ 'public' => false,
+ 'publicly_queryable' => false,
+ 'show_ui' => false,
+ 'show_in_menu' => false,
+ 'query_var' => false,
+ 'rewrite' => false,
+ 'capability_type' => 'post',
+ 'has_archive' => false,
+ 'hierarchical' => false,
+ 'supports' => array( 'author' ),
+ 'can_export' => false,
+ 'delete_with_user' => true,
+ )
+ );
+ }
+
/**
* Determines if local push notification functionality should be enabled.
* Push notifications require both the feature flag to be enabled and
@@ -85,7 +119,12 @@ class PushNotifications {
);
} catch ( Exception $e ) {
$logger = wc_get_container()->get( LegacyProxy::class )->call_function( 'wc_get_logger' );
- $logger->error( 'Error determining if PushNotifications feature should be enabled: ' . $e->getMessage() );
+
+ if ( $logger instanceof WC_Logger ) {
+ $logger->error(
+ 'Error determining if PushNotifications feature should be enabled: ' . $e->getMessage()
+ );
+ }
$this->enabled = false;
}
diff --git a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/DataStores/PushTokensDataStoreTest.php b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/DataStores/PushTokensDataStoreTest.php
index 31a019dc6d..3b17c9568f 100644
--- a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/DataStores/PushTokensDataStoreTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/DataStores/PushTokensDataStoreTest.php
@@ -50,7 +50,7 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
$push_token = new PushToken();
$push_token->set_user_id( 1 );
$push_token->set_token( 'test_token_12345' );
- $push_token->set_platform( PushToken::PLATFORM_IOS );
+ $push_token->set_platform( PushToken::PLATFORM_APPLE );
$push_token->set_device_uuid( 'device-uuid-123' );
$push_token->set_origin( PushToken::ORIGIN_WOOCOMMERCE_IOS );
@@ -108,21 +108,29 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
$data_store = new PushTokensDataStore();
$push_token = $this->create_test_push_token();
- // Verify device_uuid exists initially.
$this->assertNotNull( $push_token->get_device_uuid() );
$device_uuid = get_post_meta( $push_token->get_id(), 'device_uuid', true );
$this->assertNotEmpty( $device_uuid );
- // Convert to browser token (device_uuid becomes null).
$push_token->set_platform( PushToken::PLATFORM_BROWSER );
$push_token->set_device_uuid( null );
$data_store->update( $push_token );
- // Verify device_uuid meta is removed from database.
$device_uuid = get_post_meta( $push_token->get_id(), 'device_uuid', true );
$this->assertEmpty( $device_uuid );
}
+ /**
+ * @testdox Tests the delete method of the push tokens data store.
+ */
+ public function test_it_can_delete_push_token() {
+ $data_store = new PushTokensDataStore();
+ $push_token = $this->create_test_push_token();
+ $data_store->delete( $push_token );
+
+ $this->assertNull( get_post( $push_token->get_id() ) );
+ }
+
/**
* @testdox Tests the create method throws exception when push token data is
* incomplete.
@@ -136,7 +144,6 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
$this->expectException( InvalidArgumentException::class );
$this->expectExceptionMessage( 'Can\'t create push token because the push token data provided is invalid.' );
- $this->expectExceptionCode( 400 );
$data_store->create( $push_token );
}
@@ -152,7 +159,6 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
$this->expectException( InvalidArgumentException::class );
$this->expectExceptionMessage( 'Can\'t read push token because the push token data provided is invalid.' );
- $this->expectExceptionCode( 400 );
$data_store->read( $push_token );
}
@@ -169,7 +175,6 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
$this->expectException( Exception::class );
$this->expectExceptionMessage( 'Push token could not be found.' );
- $this->expectExceptionCode( 404 );
$data_store->read( $push_token );
}
@@ -181,7 +186,6 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
public function test_it_throws_exception_when_reading_push_token_with_wrong_post_type() {
$data_store = new PushTokensDataStore();
- // Create a regular post instead of a push_token.
$post_id = wp_insert_post(
array(
'post_title' => 'Test Post',
@@ -195,7 +199,35 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
$this->expectException( Exception::class );
$this->expectExceptionMessage( 'Push token could not be found.' );
- $this->expectExceptionCode( 404 );
+
+ $data_store->read( $push_token );
+ }
+
+ /**
+ * @testdox Tests the read method throws exception when push token metadata
+ * is malformed/missing.
+ */
+ public function test_it_throws_exception_when_reading_push_token_with_malformed_metadata() {
+ $data_store = new PushTokensDataStore();
+
+ $post_id = wp_insert_post(
+ array(
+ 'post_author' => 1,
+ 'post_type' => PushToken::POST_TYPE,
+ 'post_status' => 'private',
+ 'meta_input' => array(
+ 'platform' => PushToken::PLATFORM_APPLE,
+ 'token' => 'test_token',
+ // Missing device_uuid and origin.
+ ),
+ )
+ );
+
+ $push_token = new PushToken();
+ $push_token->set_id( $post_id );
+
+ $this->expectException( InvalidArgumentException::class );
+ $this->expectExceptionMessage( 'Can\'t read push token because the push token record is malformed.' );
$data_store->read( $push_token );
}
@@ -213,7 +245,6 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
$this->expectException( InvalidArgumentException::class );
$this->expectExceptionMessage( 'Can\'t update push token because the push token data provided is invalid.' );
- $this->expectExceptionCode( 400 );
$data_store->update( $push_token );
}
@@ -229,13 +260,12 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
$push_token->set_id( 999999 );
$push_token->set_user_id( 1 );
$push_token->set_token( 'test_token' );
- $push_token->set_platform( PushToken::PLATFORM_IOS );
+ $push_token->set_platform( PushToken::PLATFORM_APPLE );
$push_token->set_device_uuid( 'device-uuid' );
$push_token->set_origin( PushToken::ORIGIN_WOOCOMMERCE_IOS );
$this->expectException( Exception::class );
$this->expectExceptionMessage( 'Push token could not be found.' );
- $this->expectExceptionCode( 404 );
$data_store->update( $push_token );
}
@@ -247,7 +277,6 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
public function test_it_throws_exception_when_updating_push_token_with_wrong_post_type() {
$data_store = new PushTokensDataStore();
- // Create a regular post instead of a push_token.
$post_id = wp_insert_post(
array(
'post_title' => 'Test Post',
@@ -260,37 +289,328 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
$push_token->set_id( $post_id );
$push_token->set_user_id( 1 );
$push_token->set_token( 'test_token' );
- $push_token->set_platform( PushToken::PLATFORM_IOS );
+ $push_token->set_platform( PushToken::PLATFORM_APPLE );
$push_token->set_device_uuid( 'device-uuid' );
$push_token->set_origin( PushToken::ORIGIN_WOOCOMMERCE_IOS );
$this->expectException( Exception::class );
$this->expectExceptionMessage( 'Push token could not be found.' );
- $this->expectExceptionCode( 404 );
$data_store->update( $push_token );
}
/**
- * @testdox Tests the read_meta method of the push tokens data store.
+ * @testdox Tests the delete method throws exception when push token has no
+ * ID.
*/
- public function test_it_can_read_meta() {
+ public function test_it_throws_exception_when_deleting_push_token_without_id() {
$data_store = new PushTokensDataStore();
- $push_token = $this->create_test_push_token();
+ $push_token = new PushToken();
- $meta = $data_store->read_meta( $push_token );
+ $this->expectException( InvalidArgumentException::class );
+ $this->expectExceptionMessage( 'Can\'t delete push token because the push token data provided is invalid.' );
- $this->assertIsArray( $meta );
+ $data_store->delete( $push_token );
+ }
- $this->assertEquals(
+ /**
+ * @testdox Tests the delete method throws exception when the post exists but
+ * is not the correct post type.
+ */
+ public function test_it_throws_exception_when_deleting_push_token_with_wrong_post_type() {
+ $data_store = new PushTokensDataStore();
+
+ $post_id = wp_insert_post(
array(
- 'platform' => $push_token->get_platform(),
- 'token' => $push_token->get_token(),
- 'device_uuid' => $push_token->get_device_uuid(),
- 'origin' => $push_token->get_origin(),
- ),
- $meta
+ 'post_title' => 'Test Post',
+ 'post_type' => 'post',
+ 'post_status' => 'private',
+ )
);
+
+ $push_token = new PushToken();
+ $push_token->set_id( $post_id );
+
+ $this->expectException( Exception::class );
+ $this->expectExceptionMessage( 'Push token could not be found.' );
+
+ $data_store->delete( $push_token );
+ }
+
+ /**
+ * @testdox Tests the get_by_token_or_device_id method finds push token by
+ * token when user ID, platform, and origin match.
+ */
+ public function test_it_can_get_by_token_if_platform_and_user_id_matches() {
+ $data_store = new PushTokensDataStore();
+
+ $original_push_token = $this->create_test_push_token();
+
+ $new_push_token = new PushToken();
+ $new_push_token->set_user_id( $original_push_token->get_user_id() );
+ $new_push_token->set_token( $original_push_token->get_token() );
+ $new_push_token->set_platform( $original_push_token->get_platform() );
+ $new_push_token->set_origin( $original_push_token->get_origin() );
+ $new_push_token->set_device_uuid( 'different-device' );
+
+ $found_token = $data_store->get_by_token_or_device_id( $new_push_token );
+
+ $this->assertNotNull( $found_token );
+ $this->assertEquals( $original_push_token->get_id(), $found_token->get_id() );
+ $this->assertEquals( $original_push_token->get_token(), $found_token->get_token() );
+ }
+
+ /**
+ * @testdox Tests the get_by_token_or_device_id method finds push token by
+ * device UUID when user ID, platform, and origin match.
+ */
+ public function test_it_can_get_by_device_uuid_if_platform_and_user_id_matches() {
+ $data_store = new PushTokensDataStore();
+
+ $original_push_token = $this->create_test_push_token();
+
+ $new_push_token = new PushToken();
+ $new_push_token->set_user_id( $original_push_token->get_user_id() );
+ $new_push_token->set_platform( $original_push_token->get_platform() );
+ $new_push_token->set_origin( $original_push_token->get_origin() );
+ $new_push_token->set_device_uuid( $original_push_token->get_device_uuid() );
+ $new_push_token->set_token( 'different_token' );
+
+ $found_token = $data_store->get_by_token_or_device_id( $new_push_token );
+
+ $this->assertNotNull( $found_token );
+ $this->assertEquals( $original_push_token->get_id(), $found_token->get_id() );
+ $this->assertEquals( $original_push_token->get_device_uuid(), $found_token->get_device_uuid() );
+ }
+
+ /**
+ * @testdox Tests the get_by_token_or_device_id method returns null when
+ * user ID, platform, and origin match but token and device UUID don't.
+ */
+ public function test_it_cannot_get_by_token_or_device_id_if_token_and_device_do_not_match() {
+ $data_store = new PushTokensDataStore();
+
+ $original_push_token = $this->create_test_push_token();
+
+ $new_push_token = new PushToken();
+ $new_push_token->set_user_id( $original_push_token->get_user_id() );
+ $new_push_token->set_platform( $original_push_token->get_platform() );
+ $new_push_token->set_origin( $original_push_token->get_origin() );
+ $new_push_token->set_device_uuid( 'different-device' );
+ $new_push_token->set_token( 'different_token' );
+
+ $found_token = $data_store->get_by_token_or_device_id( $new_push_token );
+
+ $this->assertNull( $found_token );
+ }
+
+ /**
+ * @testdox Tests the get_by_token_or_device_id method returns null when
+ * user ID does not match.
+ */
+ public function test_it_cannot_get_by_token_or_device_id_if_user_id_does_not_match() {
+ $data_store = new PushTokensDataStore();
+
+ $original_push_token = $this->create_test_push_token();
+
+ $new_push_token = new PushToken();
+ $new_push_token->set_user_id( 999 );
+ $new_push_token->set_platform( $original_push_token->get_platform() );
+ $new_push_token->set_origin( $original_push_token->get_origin() );
+ $new_push_token->set_device_uuid( $original_push_token->get_device_uuid() );
+ $new_push_token->set_token( $original_push_token->get_token() );
+
+ $found_token = $data_store->get_by_token_or_device_id( $new_push_token );
+
+ $this->assertNull( $found_token );
+ }
+
+ /**
+ * @testdox Tests the get_by_token_or_device_id method returns null when
+ * platform does not match.
+ */
+ public function test_it_cannot_get_by_token_or_device_id_if_platform_does_not_match() {
+ $data_store = new PushTokensDataStore();
+
+ $original_push_token = $this->create_test_push_token();
+
+ $new_push_token = new PushToken();
+ $new_push_token->set_user_id( $original_push_token->get_user_id() );
+ $new_push_token->set_platform( PushToken::PLATFORM_ANDROID );
+ $new_push_token->set_origin( $original_push_token->get_origin() );
+ $new_push_token->set_device_uuid( $original_push_token->get_device_uuid() );
+ $new_push_token->set_token( $original_push_token->get_token() );
+
+ $found_token = $data_store->get_by_token_or_device_id( $new_push_token );
+
+ $this->assertNull( $found_token );
+ }
+
+ /**
+ * @testdox Tests the get_by_token_or_device_id method returns null when
+ * origin does not match.
+ */
+ public function test_it_cannot_get_by_token_or_device_id_if_origin_does_not_match() {
+ $data_store = new PushTokensDataStore();
+
+ $original_push_token = $this->create_test_push_token();
+
+ $new_push_token = new PushToken();
+ $new_push_token->set_user_id( $original_push_token->get_user_id() );
+ $new_push_token->set_platform( $original_push_token->get_platform() );
+ $new_push_token->set_origin( PushToken::ORIGIN_WOOCOMMERCE_IOS_DEV );
+ $new_push_token->set_device_uuid( $original_push_token->get_device_uuid() );
+ $new_push_token->set_token( $original_push_token->get_token() );
+
+ $found_token = $data_store->get_by_token_or_device_id( $new_push_token );
+
+ $this->assertNull( $found_token );
+ }
+
+ /**
+ * @testdox Tests that browser tokens with null device_uuid don't
+ * incorrectly match each other by empty device_uuid.
+ */
+ public function test_it_does_not_match_browser_tokens_by_empty_device_uuid() {
+ $data_store = new PushTokensDataStore();
+
+ /**
+ * Create first browser token for user.
+ */
+ $browser_token_1 = new PushToken();
+ $browser_token_1->set_user_id( 1 );
+ $browser_token_1->set_token( 'browser_token_1_' . wp_rand() );
+ $browser_token_1->set_platform( PushToken::PLATFORM_BROWSER );
+ $browser_token_1->set_device_uuid( null );
+ $browser_token_1->set_origin( PushToken::ORIGIN_WOOCOMMERCE_IOS );
+ $data_store->create( $browser_token_1 );
+
+ /**
+ * Create second browser token for same user (different browser/tab).
+ */
+ $browser_token_2 = new PushToken();
+ $browser_token_2->set_user_id( 1 );
+ $browser_token_2->set_token( 'browser_token_2_' . wp_rand() );
+ $browser_token_2->set_platform( PushToken::PLATFORM_BROWSER );
+ $browser_token_2->set_device_uuid( null );
+ $browser_token_2->set_origin( PushToken::ORIGIN_WOOCOMMERCE_IOS );
+ $data_store->create( $browser_token_2 );
+
+ /**
+ * Try to find browser_token_1 by its token - should only match itself,
+ * not browser_token_2.
+ */
+ $search_token = new PushToken();
+ $search_token->set_user_id( 1 );
+ $search_token->set_token( $browser_token_1->get_token() );
+ $search_token->set_platform( PushToken::PLATFORM_BROWSER );
+ $search_token->set_device_uuid( null );
+ $search_token->set_origin( PushToken::ORIGIN_WOOCOMMERCE_IOS );
+
+ $found_token = $data_store->get_by_token_or_device_id( $search_token );
+
+ $this->assertNotNull( $found_token, 'Should find browser_token_1 by its token value' );
+ $this->assertEquals( $browser_token_1->get_id(), $found_token->get_id(), 'Should match browser_token_1 ID' );
+ $this->assertEquals( $browser_token_1->get_token(), $found_token->get_token(), 'Should match browser_token_1 token' );
+ $this->assertNotEquals( $browser_token_2->get_id(), $found_token->get_id(), 'Should not match browser_token_2 ID' );
+
+ /**
+ * Now search with a DIFFERENT token - should return null, not match by
+ * empty device_uuid.
+ */
+ $different_token = new PushToken();
+ $different_token->set_user_id( 1 );
+ $different_token->set_platform( PushToken::PLATFORM_BROWSER );
+ $different_token->set_origin( PushToken::ORIGIN_WOOCOMMERCE_IOS );
+ $different_token->set_token(
+ wp_json_encode(
+ array(
+ 'endpoint' => 'https://example.com/push/subscription3',
+ 'keys' => array(
+ 'auth' => 'a3',
+ 'p256dh' => 'p3',
+ ),
+ )
+ )
+ );
+
+ $found = $data_store->get_by_token_or_device_id( $different_token );
+ $this->assertNull( $found, 'Should not match existing tokens by empty device_uuid' );
+ }
+
+ /**
+ * @testdox Tests the get_by_token_or_device_id method throws exception when
+ * user ID is missing.
+ */
+ public function test_it_throws_exception_when_getting_by_token_or_device_id_without_user_id() {
+ $data_store = new PushTokensDataStore();
+
+ $push_token = new PushToken();
+ $push_token->set_platform( PushToken::PLATFORM_APPLE );
+ $push_token->set_origin( PushToken::ORIGIN_WOOCOMMERCE_IOS );
+ $push_token->set_token( 'test_token' );
+ $push_token->set_device_uuid( 'test_device' );
+
+ $this->expectException( InvalidArgumentException::class );
+ $this->expectExceptionMessage( 'Can\'t retrieve push token because the push token data provided is invalid.' );
+
+ $data_store->get_by_token_or_device_id( $push_token );
+ }
+
+ /**
+ * @testdox Tests the get_by_token_or_device_id method throws exception when
+ * platform is missing.
+ */
+ public function test_it_throws_exception_when_getting_by_token_or_device_id_without_platform() {
+ $data_store = new PushTokensDataStore();
+
+ $push_token = new PushToken();
+ $push_token->set_user_id( 1 );
+ $push_token->set_origin( PushToken::ORIGIN_WOOCOMMERCE_IOS );
+ $push_token->set_token( 'test_token' );
+ $push_token->set_device_uuid( 'test_device' );
+
+ $this->expectException( InvalidArgumentException::class );
+ $this->expectExceptionMessage( 'Can\'t retrieve push token because the push token data provided is invalid.' );
+
+ $data_store->get_by_token_or_device_id( $push_token );
+ }
+
+ /**
+ * @testdox Tests the get_by_token_or_device_id method throws exception when
+ * origin is missing.
+ */
+ public function test_it_throws_exception_when_getting_by_token_or_device_id_without_origin() {
+ $data_store = new PushTokensDataStore();
+
+ $push_token = new PushToken();
+ $push_token->set_user_id( 1 );
+ $push_token->set_platform( PushToken::PLATFORM_APPLE );
+ $push_token->set_token( 'test_token' );
+ $push_token->set_device_uuid( 'test_device' );
+
+ $this->expectException( InvalidArgumentException::class );
+ $this->expectExceptionMessage( 'Can\'t retrieve push token because the push token data provided is invalid.' );
+
+ $data_store->get_by_token_or_device_id( $push_token );
+ }
+
+ /**
+ * @testdox Tests the get_by_token_or_device_id method throws exception when
+ * both token and device_uuid are missing.
+ */
+ public function test_it_throws_exception_when_getting_by_token_or_device_id_without_token_and_device_uuid() {
+ $data_store = new PushTokensDataStore();
+
+ $push_token = new PushToken();
+ $push_token->set_user_id( 1 );
+ $push_token->set_platform( PushToken::PLATFORM_APPLE );
+ $push_token->set_origin( PushToken::ORIGIN_WOOCOMMERCE_IOS );
+
+ $this->expectException( InvalidArgumentException::class );
+ $this->expectExceptionMessage( 'Can\'t retrieve push token because the push token data provided is invalid.' );
+
+ $data_store->get_by_token_or_device_id( $push_token );
}
/**
@@ -300,7 +620,6 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
public function test_it_can_create_and_read_browser_token_without_device_uuid() {
$data_store = new PushTokensDataStore();
- // Create a browser token without device_uuid.
$push_token = new PushToken();
$push_token->set_user_id( 1 );
$push_token->set_token( '{"endpoint":"https://example.com/push","keys":{"auth":"test","p256dh":"test"}}' );
@@ -311,7 +630,6 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
$this->assertNotNull( $push_token->get_id() );
- // Now try to read it back.
$read_token = new PushToken();
$read_token->set_id( $push_token->get_id() );
@@ -336,7 +654,7 @@ class PushTokensDataStoreTest extends WC_Unit_Test_Case {
$push_token = new PushToken();
$push_token->set_user_id( 1 );
$push_token->set_token( 'test_token_' . wp_rand() );
- $push_token->set_platform( PushToken::PLATFORM_IOS );
+ $push_token->set_platform( PushToken::PLATFORM_APPLE );
$push_token->set_device_uuid( 'test-device-uuid-' . wp_rand() );
$push_token->set_origin( PushToken::ORIGIN_WOOCOMMERCE_IOS );
diff --git a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Entities/PushTokenTest.php b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Entities/PushTokenTest.php
index ddd74653d1..88cd7d11da 100644
--- a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Entities/PushTokenTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Entities/PushTokenTest.php
@@ -59,9 +59,9 @@ class PushTokenTest extends WC_Unit_Test_Case {
*/
public function test_it_can_get_and_set_platform() {
$push_token = new PushToken();
- $push_token->set_platform( PushToken::PLATFORM_IOS );
+ $push_token->set_platform( PushToken::PLATFORM_APPLE );
- $this->assertEquals( PushToken::PLATFORM_IOS, $push_token->get_platform() );
+ $this->assertEquals( PushToken::PLATFORM_APPLE, $push_token->get_platform() );
}
/**
@@ -73,7 +73,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
1,
'test_token',
'test-device-uuid',
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
PushToken::ORIGIN_WOOCOMMERCE_IOS
);
@@ -89,7 +89,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
1,
'test_token',
'test-device-uuid',
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
PushToken::ORIGIN_WOOCOMMERCE_IOS
);
@@ -105,7 +105,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
null,
'test_token',
'test-device-uuid',
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
PushToken::ORIGIN_WOOCOMMERCE_IOS
);
@@ -137,7 +137,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
1,
null,
'test-device-uuid',
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
PushToken::ORIGIN_WOOCOMMERCE_IOS
);
@@ -153,7 +153,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
1,
'test_token',
null,
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
PushToken::ORIGIN_WOOCOMMERCE_IOS
);
@@ -170,7 +170,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
1,
'test_token',
'test-device-uuid',
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
PushToken::ORIGIN_WOOCOMMERCE_IOS
);
@@ -186,7 +186,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
1,
'test_token',
'test-device-uuid',
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
PushToken::ORIGIN_WOOCOMMERCE_IOS
);
@@ -202,7 +202,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
null,
'test_token',
'test-device-uuid',
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
PushToken::ORIGIN_WOOCOMMERCE_IOS
);
@@ -234,7 +234,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
1,
'test_token',
null,
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
PushToken::ORIGIN_WOOCOMMERCE_IOS
);
@@ -250,7 +250,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
1,
null,
'test-device-uuid',
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
PushToken::ORIGIN_WOOCOMMERCE_IOS
);
@@ -359,7 +359,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
1,
'test_token',
'test-device-uuid',
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
null
);
@@ -375,7 +375,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
1,
'test_token',
'test-device-uuid',
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
null
);
@@ -425,30 +425,6 @@ class PushTokenTest extends WC_Unit_Test_Case {
$this->assertEquals( PushToken::ORIGIN_WOOCOMMERCE_IOS_DEV, $push_token->get_origin() );
}
- /**
- * @testdox Tests set_id throws exception with zero.
- */
- public function test_it_throws_exception_when_setting_id_to_zero() {
- $push_token = new PushToken();
-
- $this->expectException( InvalidArgumentException::class );
- $this->expectExceptionMessage( 'ID must be a positive integer.' );
-
- $push_token->set_id( 0 );
- }
-
- /**
- * @testdox Tests set_id throws exception with negative number.
- */
- public function test_it_throws_exception_when_setting_negative_id() {
- $push_token = new PushToken();
-
- $this->expectException( InvalidArgumentException::class );
- $this->expectExceptionMessage( 'ID must be a positive integer.' );
-
- $push_token->set_id( -1 );
- }
-
/**
* @testdox Tests set_user_id throws exception with zero.
*/
@@ -561,7 +537,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
456,
'test_token_value',
'device-uuid-123',
- PushToken::PLATFORM_IOS,
+ PushToken::PLATFORM_APPLE,
PushToken::ORIGIN_WOOCOMMERCE_IOS
);
@@ -569,7 +545,7 @@ class PushTokenTest extends WC_Unit_Test_Case {
$this->assertSame( 456, $push_token->get_user_id() );
$this->assertSame( 'test_token_value', $push_token->get_token() );
$this->assertSame( 'device-uuid-123', $push_token->get_device_uuid() );
- $this->assertSame( PushToken::PLATFORM_IOS, $push_token->get_platform() );
+ $this->assertSame( PushToken::PLATFORM_APPLE, $push_token->get_platform() );
$this->assertSame( PushToken::ORIGIN_WOOCOMMERCE_IOS, $push_token->get_origin() );
}
diff --git a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/PushNotificationsTest.php b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/PushNotificationsTest.php
index fac883ec70..d9f1d7b489 100644
--- a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/PushNotificationsTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/PushNotificationsTest.php
@@ -6,10 +6,12 @@ namespace Automattic\WooCommerce\Tests\Internal\PushNotifications;
use Automattic\Jetpack\Connection\Manager as JetpackConnectionManager;
use Automattic\WooCommerce\Internal\Features\FeaturesController;
+use Automattic\WooCommerce\Internal\PushNotifications\Entities\PushToken;
use Automattic\WooCommerce\Internal\PushNotifications\PushNotifications;
use Automattic\WooCommerce\Proxies\LegacyProxy;
use Exception;
use PHPUnit\Framework\MockObject\MockObject;
+use WC_Logger;
use WC_Unit_Test_Case;
/**
@@ -106,20 +108,15 @@ class PushNotificationsTest extends WC_Unit_Test_Case {
* enablement check.
*/
public function test_it_logs_error_when_jetpack_connection_check_throws_exception() {
- // phpcs:disable Squiz.Commenting
- $fake_logger = new class() {
- public $errors = array();
-
- public function error( $message, $data = array() ) {
- $this->errors[] = array(
- 'message' => $message,
- 'data' => $data,
- );
- }
- };
- // phpcs:enable Squiz.Commenting
-
- $this->register_legacy_proxy_function_mocks( array( 'wc_get_logger' => fn () => $fake_logger ) );
+ $logger_mock = $this->createMock( WC_Logger::class );
+ $logger_mock->expects( $this->once() )
+ ->method( 'error' )
+ ->with(
+ $this->stringContains( 'Error determining if PushNotifications feature should be enabled' ),
+ $this->anything()
+ );
+
+ $this->register_legacy_proxy_function_mocks( array( 'wc_get_logger' => fn () => $logger_mock ) );
$this->set_up_features_controller_mock( true );
$this->set_up_jetpack_connection_manager_mock( array( 'is_connected' ) );
@@ -132,13 +129,6 @@ class PushNotificationsTest extends WC_Unit_Test_Case {
$result = $push_notifications->should_be_enabled();
$this->assertFalse( $result, 'Should be disabled when exception is thrown' );
- $this->assertCount( 1, $fake_logger->errors, 'Should have logged exactly one error' );
- $this->assertStringContainsString( 'Connection check failed', $fake_logger->errors[0]['message'] );
-
- $this->assertStringContainsString(
- 'Error determining if PushNotifications feature should be enabled',
- $fake_logger->errors[0]['message']
- );
}
/**
@@ -175,6 +165,59 @@ class PushNotificationsTest extends WC_Unit_Test_Case {
$this->assertTrue( $push_notifications_2->should_be_enabled(), 'Should return cached true value' );
}
+ /**
+ * @testdox Tests that register() hooks register_post_types to init when enabled.
+ */
+ public function test_it_hooks_register_post_types_when_enabled() {
+ $this->set_up_jetpack_connection_manager_mock( array( 'is_connected' ) );
+
+ $this->jetpack_connection_manager_mock
+ ->expects( $this->once() )
+ ->method( 'is_connected' )
+ ->willReturn( true );
+
+ $push_notifications = new PushNotifications();
+ $push_notifications->register();
+
+ $callback_priority = has_action( 'init', array( $push_notifications, 'register_post_types' ) );
+
+ $this->assertTrue( (bool) $callback_priority, 'register_post_types should be hooked to init' );
+ $this->assertEquals( 10, $callback_priority, 'register_post_types should have priority 10' );
+ }
+
+ /**
+ * @testdox Tests that register_post_types() registers push_token post type with correct properties.
+ */
+ public function test_it_registers_push_token_post_type_with_correct_properties() {
+ $push_notifications = new PushNotifications();
+ $push_notifications->register_post_types();
+
+ $this->assertTrue( post_type_exists( PushToken::POST_TYPE ), 'Push token post type should be registered' );
+
+ $post_type_object = get_post_type_object( PushToken::POST_TYPE );
+
+ $this->assertNotNull( $post_type_object );
+ $this->assertFalse( $post_type_object->public );
+ $this->assertFalse( $post_type_object->publicly_queryable );
+ $this->assertTrue( $post_type_object->delete_with_user );
+ }
+
+ /**
+ * @testdox Tests that push_token post type is not registered when disabled.
+ */
+ public function test_it_does_not_register_push_token_post_type_when_disabled() {
+ $this->set_up_features_controller_mock( false );
+ $this->set_up_jetpack_connection_manager_mock( array( 'is_connected' ) );
+
+ $push_notifications = new PushNotifications();
+ $push_notifications->register();
+
+ $this->assertFalse(
+ has_action( 'init', array( $push_notifications, 'register_post_types' ) ),
+ 'register_post_types should not be hooked to init when disabled'
+ );
+ }
+
/**
* Sets up the FeaturesController mock.
*