Fix unit tests for object cache to accomodate recent changes.

This commit is contained in:
Vedanshu Jain 2023-01-25 12:29:55 +05:30
parent 93bc959957
commit 7619c335ff
7 changed files with 145 additions and 356 deletions

View File

@ -49,7 +49,7 @@ trait CacheNameSpaceTrait {
* @since 3.9.0
*/
public static function invalidate_cache_group( $group ) {
wp_cache_set( 'wc_' . $group . '_cache_prefix', microtime(), $group );
return wp_cache_set( 'wc_' . $group . '_cache_prefix', microtime(), $group );
}
/**

View File

@ -244,7 +244,6 @@ abstract class ObjectCache {
if ( null === $object ) {
return null;
}
$this->set( $object, $id, $expiration );
$data = $this->last_cached_data;
}

View File

@ -63,6 +63,6 @@ class WPCacheEngine implements CacheEngine {
* @return bool True if the group is deleted successfully, false otherwise.
*/
public function delete_cache_group( string $group = '' ): bool {
self::invalidate_cache_group( $group );
return self::invalidate_cache_group( $group );
}
}

View File

@ -26,7 +26,21 @@ class CacheExceptionTest extends \WC_Unit_Test_Case {
public function get_object_type(): string {
return 'the_type';
}
};
protected function get_object_id( $object ) {
}
protected function validate( $object ): ?array {
}
/**
* @param $id
*
* @return mixed
*/
protected function get_from_datastore( $id ) {
// TODO: Implement get_from_datastore() method.
}};
// phpcs:enable Squiz.Commenting
}

View File

@ -1,63 +0,0 @@
<?php
namespace Automattic\WooCommerce\Tests\Caching;
use Automattic\WooCommerce\Caching\CacheEngine;
use Automattic\WooCommerce\Utilities\ArrayUtil;
/**
* An implementation of CacheEngine that simply stores cached objects in an array.
*/
class InMemoryObjectCacheEngine implements CacheEngine {
/**
* The cached objects.
*
* @var array
*/
public $cache = array();
/**
* Whether calls to 'cache_object' will succeed or not.
*
* @var bool
*/
public $caching_succeeds = true;
/**
* Value of the expiration time that was passed to 'cache_object' the last time it was called.
*
* @var int
*/
public $last_expiration;
// phpcs:disable Squiz.Commenting
public function get_cached_object( string $key ) {
return ArrayUtil::get_value_or_default( $this->cache, $key, null );
}
public function cache_object( string $key, $object, int $expiration ): bool {
if ( ! $this->caching_succeeds ) {
return false;
}
$this->cache[ $key ] = $object;
$this->last_expiration = $expiration;
return true;
}
public function delete_cached_object( string $key ): bool {
if ( array_key_exists( $key, $this->cache ) ) {
unset( $this->cache[ $key ] );
return true;
}
return false;
}
public function is_cached( $key ): bool {
return array_key_exists( $key, $this->cache );
}
// phpcs:enable Squiz.Commenting
}

View File

@ -17,4 +17,25 @@ class InvalidObjectCacheClass extends ObjectCache {
}
// phpcs:enable Squiz.Commenting
/**
* @inheritDoc
*/
protected function get_object_id( $object ) {
// TODO: Implement get_object_id() method.
}
/**
* @inheritDoc
*/
protected function validate( $object ): ?array {
// TODO: Implement validate() method.
}
/**
* @inheritDoc
*/
protected function get_from_datastore( $id ) {
// TODO: Implement get_from_datastore() method.
}
}

View File

@ -5,7 +5,7 @@ namespace Automattic\WooCommerce\Tests\Caching;
use Automattic\WooCommerce\Caching\CacheException;
use Automattic\WooCommerce\Caching\ObjectCache;
use Automattic\WooCommerce\Caching\CacheEngine;
use Automattic\WooCommerce\Caching\WpCacheEngine;
use Automattic\WooCommerce\Caching\WPCacheEngine;
/**
* Tests for the ObjectCache class.
@ -19,23 +19,10 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
*/
private $sut;
/**
* The cache engine to use.
*
* @var CacheEngine
*/
private $cache_engine;
/**
* Runs before each test.
*/
public function setUp(): void {
$cache_engine = new InMemoryObjectCacheEngine();
$this->cache_engine = $cache_engine;
$container = wc_get_container();
$container->replace( WpCacheEngine::class, $cache_engine );
$this->reset_container_resolutions();
// phpcs:disable Squiz.Commenting
@ -50,9 +37,22 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
$this->random_string_index++;
return 'random_' . $this->random_string_index;
}
};
protected function get_object_id( $object ) {
return null;
}
protected function validate( $object ): ?array {
return null;
}
protected function get_from_datastore( $id ) {
return null;
}
};
// phpcs:enable Squiz.Commenting
$this->sut->flush();
}
/**
@ -152,12 +152,10 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
$this->assertTrue( $result );
$expected_prefix = 'woocommerce_object_cache|the_type|random_1|';
$this->assertEquals( $expected_prefix, get_option( 'wp_object_cache_key_prefix_the_type' ) );
$expected_prefix = \WC_Cache_Helper::get_cache_prefix( 'the_type' );
$key = $expected_prefix . 'the_id';
$expected_cached = array( 'data' => $object );
$this->assertEquals( $expected_cached, $this->cache_engine->cache[ $key ] );
$this->assertEquals( $object, wp_cache_get( $key, 'the_type' ) );
}
/**
@ -169,30 +167,12 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
$object_2 = array( 1, 2, 3, 4 );
$this->sut->set( $object_2, 9999 );
$prefix = 'woocommerce_object_cache|the_type|random_1|';
$prefix = \WC_Cache_Helper::get_cache_prefix( 'the_type' );
$key_1 = $prefix . 'the_id_1';
$expected_cached = array( 'data' => $object_1 );
$this->assertEquals( $expected_cached, $this->cache_engine->cache[ $key_1 ] );
$this->assertEquals( $object_1, wp_cache_get( $key_1, 'the_type' ) );
$key_2 = $prefix . '9999';
$expected_cached = array( 'data' => $object_2 );
$this->assertEquals( $expected_cached, $this->cache_engine->cache[ $key_2 ] );
}
/**
* @testdox 'set' uses the default expiration value if no explicit value is passed.
*/
public function test_set_with_default_expiration() {
$this->sut->set( array( 'foo' ), 'the_id' );
$this->assertEquals( $this->sut->get_default_expiration_value(), $this->cache_engine->last_expiration );
}
/**
* @testdox 'set' uses the explicitly passed expiration value.
*/
public function test_set_with_explicit_expiration() {
$this->sut->set( array( 'foo' ), 'the_id', 1234 );
$this->assertEquals( 1234, $this->cache_engine->last_expiration );
$this->assertEquals( $object_2, wp_cache_get( $key_2, 'the_type' ) );
}
/**
@ -223,24 +203,28 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
protected function get_random_string(): string {
return 'random';
}
protected function validate( $object ): ?array {
return null;
}
protected function get_from_datastore( $id ) {
return null;
}
};
// phpcs:enable Squiz.Commenting
$sut->set( array( 'id' => 1234 ) );
$this->assertEquals( 'woocommerce_object_cache|the_type|random|1235', array_keys( $this->cache_engine->cache )[0] );
$this->assertEquals( array( 'id' => 1234 ), $sut->get( '1235' ) );
}
/**
* @testdox 'update_if_cached' does nothing if no object is cached with the passed (or obtained) id.
*
* @testWith [1234]
* [null]
*
* @param ?int $id Id to pass to update_if_cached.
*/
public function test_update_if_cached_does_nothing_for_not_cached_id( ?int $id ) {
public function test_update_if_cached_does_nothing_for_not_cached_id() {
$id = 1234;
// phpcs:disable Squiz.Commenting
$sut = new class() extends ObjectCache {
@ -251,24 +235,30 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
protected function get_object_id( $object ) {
return $object['id'];
}
protected function validate( $object ): ?array {
return null;
}
protected function get_from_datastore( $id ) {
return null;
}
};
// phpcs:enable Squiz.Commenting
$result = $sut->update_if_cached( array( 'id' => 1234 ), $id );
$this->assertFalse( $result );
$this->assertEmpty( $this->cache_engine->cache );
$this->assertEmpty( $sut->get( $id ) );
}
/**
* @testdox 'update_if_cached' updates an already cached object the same way as 'set'.
*
* @testWith [1234]
* [null]
*
* @param ?int $id Id to pass to update_if_cached.
*/
public function test_update_if_cached_updates_already_cached_object( ?int $id ) {
public function test_update_if_cached_updates_already_cached_object() {
$id = 1234;
// phpcs:disable Squiz.Commenting
$sut = new class() extends ObjectCache {
@ -283,12 +273,20 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
protected function get_random_string(): string {
return 'random';
}
protected function validate( $object ): ?array {
return null;
}
protected function get_from_datastore( $id ) {
return null;
}
};
// phpcs:enable Squiz.Commenting
$sut->set( array( 'id' => 1234 ), $id );
$this->assertEquals( 'woocommerce_object_cache|the_type|random|1234', array_keys( $this->cache_engine->cache )[0] );
$this->assertEquals( array( 'id' => 1234 ), $sut->get( $id ) );
$new_value = array(
'id' => 1234,
@ -296,7 +294,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
);
$result = $sut->update_if_cached( $new_value, $id );
$this->assertTrue( $result );
$this->assertEquals( $this->cache_engine->cache['woocommerce_object_cache|the_type|random|1234'], array( 'data' => $new_value ) );
$this->assertEquals( $new_value, $sut->get( $id ) );
}
/**
@ -309,33 +307,6 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
$this->sut->update_if_cached( array( 'foo' ) );
}
/**
* @testdox 'set' caches the value returned by 'serialize'.
*/
public function test_set_with_custom_serialization() {
$object = array( 'foo' );
// phpcs:disable Squiz.Commenting
$sut = new class() extends ObjectCache {
public function get_object_type(): string {
return 'the_type';
}
protected function serialize( $object ): array {
return array( 'the_data' => $object );
}
};
// phpcs:enable Squiz.Commenting
$sut->set( $object, 1234 );
$cached = array_values( $this->cache_engine->cache )[0];
$expected = array( 'the_data' => $object );
$this->assertEquals( $expected, $cached );
}
/**
* @testdox 'set' throws an exception if 'validate' returns errors.
*
@ -365,6 +336,14 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
protected function validate( $object ): array {
return $this->errors;
}
protected function get_from_datastore( $id ) {
return null;
}
protected function get_object_id( $object ) {
return $object['id'];
}
};
// phpcs:enable Squiz.Commenting
@ -446,26 +425,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
$expected = array( 'id' => 'the_id' );
$this->assertEquals( $expected, $result );
$this->assertEquals( array( 'data' => $expected ), array_values( $this->cache_engine->cache )[0] );
$this->assertEquals( $this->sut->get_default_expiration_value(), $this->cache_engine->last_expiration );
}
/**
* @testdox 'get' uses the passed object retrieval callback if there's no object cached under the passed id, and caches the object retrieved using the passed expiration value.
*/
public function test_try_getting_not_cached_object_with_callback_and_explicit_expiration() {
$expiration = 1234;
$callback = function( $id ) {
return array( 'id' => $id );
};
$result = $this->sut->get( 'the_id', $expiration, $callback );
$expected = array( 'id' => 'the_id' );
$this->assertEquals( $expected, $result );
$this->assertEquals( array( 'data' => $expected ), array_values( $this->cache_engine->cache )[0] );
$this->assertEquals( $expiration, $this->cache_engine->last_expiration );
$this->assertEquals( $expected, $this->sut->get( 'the_id' ) );
}
/**
@ -482,6 +442,13 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
protected function get_from_datastore( $id ) {
return array( 'id' => $id );
}
protected function get_object_id( $object ) {
}
protected function validate( $object ) : ?array {
return null;
}
};
// phpcs:enable Squiz.Commenting
@ -490,63 +457,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
$expected = array( 'id' => 'the_id' );
$this->assertEquals( $expected, $result );
$this->assertEquals( array( 'data' => $expected ), array_values( $this->cache_engine->cache )[0] );
$this->assertEquals( $this->sut->get_default_expiration_value(), $this->cache_engine->last_expiration );
}
/**
* @testdox 'get' uses the 'get_from_datastore' method if there's no object cached under the passed id, and caches the object retrieved using the passed expiration value.
*/
public function test_try_getting_not_cached_object_get_from_datastore_implemented_and_explicit_expiration() {
$expiration = 1234;
// phpcs:disable Squiz.Commenting
$sut = new class() extends ObjectCache {
public function get_object_type(): string {
return 'the_type';
}
protected function get_from_datastore( $id ) {
return array( 'id' => $id );
}
};
// phpcs:enable Squiz.Commenting
$result = $sut->get( 'the_id', $expiration );
$expected = array( 'id' => 'the_id' );
$this->assertEquals( $expected, $result );
$this->assertEquals( array( 'data' => $expected ), array_values( $this->cache_engine->cache )[0] );
$this->assertEquals( $expiration, $this->cache_engine->last_expiration );
}
/**
* @testdox 'get' applies 'deserialize' to the object returned by the cache engine before returning it.
*/
public function test_custom_deserialization() {
// phpcs:disable Squiz.Commenting
$sut = new class() extends ObjectCache {
public function get_object_type(): string {
return 'the_type';
}
protected function deserialize( array $serialized ) {
$object = $serialized['data'];
$object[] = 3;
return $object;
}
};
// phpcs:enable Squiz.Commenting
$sut->set( array( 1, 2 ), 'the_id' );
$result = $sut->get( 'the_id' );
$expected = array( 1, 2, 3 );
$this->assertEquals( $expected, $result );
$this->assertEquals( $expected, $sut->get( 'the_id' ) );
}
/**
@ -572,13 +483,15 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
public function test_flush() {
$this->sut->set( array( 'foo' ), 'the_id' );
$current_prefix_key = \WC_Cache_Helper::get_cache_prefix( 'the_type' );
$this->sut->flush();
$this->assertFalse( get_option( 'wp_object_cache_key_prefix_the_type' ) );
$this->assertFalse( $this->sut->is_cached( 'the_id' ) );
$expected_new_prefix = \WC_Cache_Helper::get_cache_prefix( 'the_type' );
$this->assertNotEquals( $current_prefix_key, $expected_new_prefix );
$this->sut->set( array( 'bar' ), 'the_id_2' );
$expected_new_prefix = 'woocommerce_object_cache|the_type|random_2|';
$this->assertEquals( $expected_new_prefix, get_option( 'wp_object_cache_key_prefix_the_type' ) );
$this->assertEquals( $expected_new_prefix, \WC_Cache_Helper::get_cache_prefix( 'the_type' ) );
$this->assertFalse( $this->sut->is_cached( 'the_id' ) );
$this->assertTrue( $this->sut->is_cached( 'the_id_2' ) );
}
@ -587,7 +500,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
* @testdox A custom cache engine instance can be used by overriding 'get_cache_engine_instance'.
*/
public function test_custom_cache_engine_via_protected_method() {
$engine = new InMemoryObjectCacheEngine();
$engine = new WPCacheEngine();
// phpcs:disable Squiz.Commenting
@ -606,6 +519,16 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
protected function get_cache_engine_instance(): CacheEngine {
return $this->engine;
}
protected function get_object_id( $object ) {
}
protected function validate( $object ): ?array {
return null;
}
protected function get_from_datastore( $id ) {
}
};
// phpcs:enable Squiz.Commenting
@ -613,18 +536,33 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
$object = array( 'foo' );
$sut->set( $object, 'the_id' );
$expected_cached = array( 'data' => $object );
$this->assertEquals( $expected_cached, array_values( $engine->cache )[0] );
$this->assertEquals( $object, $sut->get( 'the_id' ) );
}
/**
* @testdox A custom cache engine instance can be used via 'wc_object_cache_get_engine' filter.
*/
public function test_custom_cache_engine_via_hook() {
$engine = new InMemoryObjectCacheEngine();
$engine = new class extends WPCacheEngine {};
$engine_passed_to_filter = null;
$cache_passed_to_filter = null;
$sut = new class() extends ObjectCache {
public function get_object_type(): string {
return 'the_type';
}
protected function get_object_id( $object ) {
}
protected function validate( $object ): ?array {
return null;
}
protected function get_from_datastore( $id ) {
}
};
add_filter(
'wc_object_cache_get_engine',
function( $old_engine, $cache ) use ( $engine, &$engine_passed_to_filter, &$cache_passed_to_filter ) {
@ -637,131 +575,11 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
);
$object = array( 'foo' );
$this->sut->set( $object, 'the_id' );
$sut->set( $object, 'the_id' );
$expected_cached = array( 'data' => $object );
$this->assertEquals( $expected_cached, array_values( $engine->cache )[0] );
$this->assertEquals( $object, $sut->get( 'the_id' ) );
$this->assertEquals( $engine_passed_to_filter, wc_get_container()->get( WpCacheEngine::class ) );
$this->assertEquals( $cache_passed_to_filter, $this->sut );
}
/**
* @testdox 'woocommerce_after_serializing_{type}_for_caching' allows to modify the serialized object before being cached.
*/
public function test_modifying_serialized_object_via_filter() {
$object_passed_to_filter = null;
$id_passed_to_filter = null;
add_filter(
'woocommerce_after_serializing_the_type_for_caching',
function( $data, $object, $id ) use ( &$object_passed_to_filter, &$id_passed_to_filter ) {
$object_passed_to_filter = $object;
$id_passed_to_filter = $id;
$data['foo'] = 'bar';
return $data;
},
10,
3
);
$object = array( 'fizz' );
$this->sut->set( $object, 'the_id' );
$expected_cached = array(
'data' => $object,
'foo' => 'bar',
);
$this->assertEquals( $expected_cached, array_values( $this->cache_engine->cache )[0] );
$this->assertEquals( $object, $object_passed_to_filter );
$this->assertEquals( 'the_id', $id_passed_to_filter );
}
/**
* @testdox 'woocommerce_after_deserializing_{type}_from_cache' allows to modify the deserialized object before it's returned by 'get'.
*/
public function test_modifying_deserialized_object_via_filter() {
$object_passed_to_filter = null;
$id_passed_to_filter = null;
$data_passed_to_filter = null;
$original_object = array( 'foo' );
$replacement_object = array( 'bar' );
add_filter(
'woocommerce_after_deserializing_the_type_from_cache',
function( $object, $data, $id ) use ( &$object_passed_to_filter, &$id_passed_to_filter, &$data_passed_to_filter, $replacement_object ) {
$object_passed_to_filter = $object;
$id_passed_to_filter = $id;
$data_passed_to_filter = $data;
return $replacement_object;
},
10,
3
);
$this->sut->set( $original_object, 'the_id' );
$retrieved_object = $this->sut->get( 'the_id' );
$this->assertEquals( $replacement_object, $retrieved_object );
$this->assertEquals( $original_object, $object_passed_to_filter );
$this->assertEquals( array( 'data' => $original_object ), $data_passed_to_filter );
$this->assertEquals( 'the_id', $id_passed_to_filter );
}
/**
* @testdox 'remove' triggers the 'woocommerce_after_removing_{type}_from_cache' action.
*
* @testWith [true]
* [false]
*
* @param bool $operation_succeeds Whether the removal operation succeeds or not.
*/
public function test_action_triggered_on_object_removed_from_cache( bool $operation_succeeds ) {
$id_passed_to_action = null;
$result_passed_to_action = null;
add_action(
'woocommerce_after_removing_the_type_from_cache',
function( $id, $result ) use ( &$id_passed_to_action, &$result_passed_to_action ) {
$id_passed_to_action = $id;
$result_passed_to_action = $result;
},
10,
2
);
$this->sut->set( array( 'foo' ), 'the_id' );
$this->sut->remove( $operation_succeeds ? 'the_id' : 'INVALID_ID' );
$this->assertEquals( $operation_succeeds ? 'the_id' : 'INVALID_ID', $id_passed_to_action );
$this->assertEquals( $operation_succeeds, $result_passed_to_action );
}
/**
* @testdox 'flush' triggers the 'woocommerce_after_flushing_{type}_cache' action.
*/
public function test_action_triggered_on_cache_flushed() {
$cache_passed_to_action = null;
$engine_passed_to_action = null;
add_action(
'woocommerce_after_flushing_the_type_cache',
function( $cache, $engine ) use ( &$cache_passed_to_action, &$engine_passed_to_action ) {
$cache_passed_to_action = $cache;
$engine_passed_to_action = $engine;
},
10,
2
);
$this->sut->flush();
$this->assertEquals( $this->sut, $cache_passed_to_action );
$this->assertEquals( $this->cache_engine, $engine_passed_to_action );
$this->assertEquals( $engine_passed_to_filter, wc_get_container()->get( WPCacheEngine::class ) );
$this->assertEquals( $cache_passed_to_filter, $sut );
}
}