Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions Tests/Unit/classes/Tools/InternalStateList/sharedList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);

namespace Imagify\Tests\Unit\classes\Tools\InternalStateList;

use Imagify\Tests\Unit\TestCase;
use Imagify\Tools\InternalStateList;

/**
* Tests for \Imagify\Tools\InternalStateList static methods.
*
* Locks the canonical lists so any accidental removal is caught immediately.
*
* @covers \Imagify\Tools\InternalStateList
* @group Tools
*/
class Test_SharedList extends TestCase {

/**
* Tests that get_bulk_transients() returns the expected canonical array.
*/
public function testGetBulkTransientsReturnsExpectedArray(): void {
$expected = [
'imagify_custom-folders_optimize_running',
'imagify_wp_optimize_running',
'imagify_bulk_optimization_complete',
'imagify_missing_next_gen_total',
'imagify_bulk_optimization_result',
'imagify_bulk_optimization_infos',
'imagify_bulk_optimization_level',
];

$this->assertSame( $expected, InternalStateList::get_bulk_transients() );
}

/**
* Asserts that user-data/account transients are NOT present in the bulk list.
*/
public function testGetBulkTransientsDoesNotContainUserCacheTransients(): void {
$user_cache_transients = [
'imagify_user',
'imagify_user_cache',
'imagify_user_images_count',
'imagify_large_library',
'imagify_attachments_number_modal',
'imagify_stat_without_next_gen',
'imagify_max_image_size',
'imagify_check_licence_1',
'imagify_check_api_version',
'imagify_check_api_key_validity',
'imagify_settings',
'imagify_data',
];

$bulk_transients = InternalStateList::get_bulk_transients();

foreach ( $user_cache_transients as $cache_transient ) {
$this->assertNotContains( $cache_transient, $bulk_transients );
}
}

/**
* Tests that get_locked_transient_patterns() returns plain (unescaped) LIKE templates.
*/
public function testGetLockedTransientPatternsReturnsExpectedArray(): void {
$expected = [
'_transient_%imagify-auto-optimize-%',
'_transient_%imagify_rpc_%',
'_transient_imagify_%_process_locked',
'_site_transient_imagify_%_process_lock%',
];

$this->assertSame( $expected, InternalStateList::get_locked_transient_patterns() );
}

/**
* Tests that get_scheduler_hooks() returns the expected canonical array.
*/
public function testGetSchedulerHooksReturnsExpectedArray(): void {
$expected = [
'imagify_optimize_media',
'imagify_convert_next_gen',
];

$this->assertSame( $expected, InternalStateList::get_scheduler_hooks() );
}
}
243 changes: 243 additions & 0 deletions Tests/Unit/classes/Tools/ResetInternalState/reset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
<?php
declare(strict_types=1);

namespace Imagify\Tests\Unit\classes\Tools\ResetInternalState;

use Imagify\Tests\Unit\TestCase;
use Imagify\Tools\ResetInternalState;
use Mockery;
use Brain\Monkey\Functions;

/**
* Tests for \Imagify\Tools\ResetInternalState::reset().
*
* @covers \Imagify\Tools\ResetInternalState::reset
* @group Tools
*/
class Test_Reset extends TestCase {

/**
* Mockery mock for $wpdb.
*
* @var \Mockery\MockInterface
*/
private $wpdb;

/**
* Sets up the test fixture.
*
* @inheritDoc
*/
public function setUp(): void {
parent::setUp();

// Build a Mockery spy for $wpdb and expose it globally.
$this->wpdb = Mockery::mock( 'wpdb' );
$this->wpdb->options = 'wp_options';
$this->wpdb->sitemeta = 'wp_sitemeta';

// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$GLOBALS['wpdb'] = $this->wpdb;
}

/**
* Tears down the test fixture.
*
* @inheritDoc
*/
public function tearDown(): void {
unset( $GLOBALS['wpdb'] );

parent::tearDown();
}

/**
* Tests that reset() calls delete_transient() with every bulk transient name.
*/
public function testDeletesBulkTransients(): void {
$this->wpdb->shouldReceive( 'esc_like' )->andReturnUsing(
function ( string $value ): string {
return str_replace( [ '\\', '%', '_' ], [ '\\\\', '\\%', '\\_' ], $value );
}
);
$this->wpdb->shouldReceive( 'prepare' )->andReturn( 'PREPARED_SQL' );
$this->wpdb->shouldReceive( 'query' )->andReturn( 1 );

Functions\when( 'is_multisite' )->justReturn( false );
Functions\when( 'delete_transient' )->justReturn( true );

$deleted = [];
Functions\when( 'delete_transient' )->alias(
function ( string $transient ) use ( &$deleted ) {
$deleted[] = $transient;
}
);

( new ResetInternalState() )->reset();

$expected_bulk = [
'imagify_custom-folders_optimize_running',
'imagify_wp_optimize_running',
'imagify_bulk_optimization_complete',
'imagify_missing_next_gen_total',
'imagify_bulk_optimization_result',
'imagify_bulk_optimization_infos',
'imagify_bulk_optimization_level',
];

foreach ( $expected_bulk as $transient ) {
$this->assertContains( $transient, $deleted, "delete_transient() was not called with '{$transient}'" );
}
}

/**
* Tests that reset() does NOT call delete_transient() for user/account cache transients.
*/
public function testDoesNotDeleteUserCacheTransients(): void {
$this->wpdb->shouldReceive( 'esc_like' )->andReturnUsing(
function ( string $value ): string {
return str_replace( [ '\\', '%', '_' ], [ '\\\\', '\\%', '\\_' ], $value );
}
);
$this->wpdb->shouldReceive( 'prepare' )->andReturn( 'PREPARED_SQL' );
$this->wpdb->shouldReceive( 'query' )->andReturn( 1 );

Functions\when( 'is_multisite' )->justReturn( false );

$deleted = [];
Functions\when( 'delete_transient' )->alias(
function ( string $transient ) use ( &$deleted ) {
$deleted[] = $transient;
}
);

( new ResetInternalState() )->reset();

$user_cache_transients = [
'imagify_user',
'imagify_user_cache',
'imagify_user_images_count',
'imagify_large_library',
'imagify_attachments_number_modal',
'imagify_stat_without_next_gen',
'imagify_max_image_size',
'imagify_check_licence_1',
'imagify_check_api_version',
'imagify_settings',
'imagify_data',
];

foreach ( $user_cache_transients as $transient ) {
$this->assertNotContains( $transient, $deleted, "delete_transient() must NOT be called with '{$transient}'" );
}
}

/**
* Tests that reset() builds LIKE patterns via esc_like() and issues a query for each against wp_options.
*/
public function testRunsLikePatternQueryAgainstOptions(): void {
Functions\when( 'delete_transient' )->justReturn( true );
Functions\when( 'is_multisite' )->justReturn( false );

// Simulate esc_like(): escape \, %, and _ with a leading backslash.
$this->wpdb->shouldReceive( 'esc_like' )
->andReturnUsing(
function ( string $value ): string {
return str_replace( [ '\\', '%', '_' ], [ '\\\\', '\\%', '\\_' ], $value );
}
);

$patterns_queried = [];

$this->wpdb->shouldReceive( 'prepare' )
->andReturnUsing(
function ( string $sql, string $pattern ) use ( &$patterns_queried ) {
$patterns_queried[] = $pattern;
return 'PREPARED_SQL';
}
);

$this->wpdb->shouldReceive( 'query' )->times( 4 )->andReturn( 0 );

( new ResetInternalState() )->reset();

// Expected: raw pattern parts esc_like'd, reassembled with % wildcards.
$expected_patterns = [
'\_transient\_%imagify-auto-optimize-%',
'\_transient\_%imagify\_rpc\_%',
'\_transient\_imagify\_%\_process\_locked',
'\_site\_transient\_imagify\_%\_process\_lock%',
];

foreach ( $expected_patterns as $pattern ) {
$this->assertContains( $pattern, $patterns_queried, "No query was prepared for pattern '{$pattern}'" );
}
}

/**
* Tests that reset() runs a second query against sitemeta when is_multisite() is true.
*/
public function testRunsSitemetaQueryOnMultisite(): void {
Functions\when( 'delete_transient' )->justReturn( true );
Functions\when( 'is_multisite' )->justReturn( true );

// Accept any esc_like() call (options-pattern parts + the explicit sitemeta prefix).
$this->wpdb->shouldReceive( 'esc_like' )
->andReturnUsing(
function ( string $value ): string {
return str_replace( [ '\\', '%', '_' ], [ '\\\\', '\\%', '\\_' ], $value );
}
);

$this->wpdb->shouldReceive( 'prepare' )->andReturn( 'PREPARED_SQL' );

$query_calls = 0;
$this->wpdb->shouldReceive( 'query' )
->andReturnUsing(
function () use ( &$query_calls ) {
$query_calls++;
return 0;
}
);

( new ResetInternalState() )->reset();

// 4 options-pattern queries + 1 sitemeta query = 5 total.
$this->assertGreaterThanOrEqual( 5, $query_calls, 'Expected at least 5 wpdb::query() calls on multisite (4 options + 1 sitemeta)' );
}

/**
* Tests that reset() skips as_unschedule_all_actions() when ActionScheduler is not loaded.
*
* As_unschedule_all_actions() does not exist in the test environment, so
* function_exists() naturally returns false — no stubbing needed.
* The 4 wpdb::query() calls for the options LIKE patterns are the proof that
* reset() ran to completion without errors.
*/
public function testSkipsSchedulerWhenFunctionNotExists(): void {
$this->wpdb->shouldReceive( 'esc_like' )
->andReturnUsing(
function ( string $value ): string {
return str_replace( [ '\\', '%', '_' ], [ '\\\\', '\\%', '\\_' ], $value );
}
);
$this->wpdb->shouldReceive( 'prepare' )->andReturn( 'PREPARED_SQL' );

$query_calls = 0;
$this->wpdb->shouldReceive( 'query' )
->andReturnUsing(
function () use ( &$query_calls ) {
$query_calls++;
return 0;
}
);

Functions\when( 'delete_transient' )->justReturn( true );
Functions\when( 'is_multisite' )->justReturn( false );

( new ResetInternalState() )->reset();

// 4 options-pattern queries prove reset() ran to completion.
$this->assertSame( 4, $query_calls );
}
}
48 changes: 48 additions & 0 deletions Tests/Unit/classes/Tools/Subscriber/getSubscribedEvents.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);

namespace Imagify\Tests\Unit\classes\Tools\Subscriber;

use Imagify\Tests\Unit\TestCase;
use Imagify\Tools\Subscriber;
use Imagify\Tools\ResetInternalState;
use Mockery;

/**
* Tests for \Imagify\Tools\Subscriber::get_subscribed_events().
*
* @covers \Imagify\Tools\Subscriber::get_subscribed_events
* @group Tools
*/
class Test_GetSubscribedEvents extends TestCase {

/**
* Tests that get_subscribed_events() registers the AJAX reset action.
*/
public function testRegistersAjaxResetAction(): void {
$events = Subscriber::get_subscribed_events();

$this->assertArrayHasKey( 'wp_ajax_imagify_reset_internal_state', $events );
$this->assertSame( 'reset_internal_state', $events['wp_ajax_imagify_reset_internal_state'] );
}

/**
* Tests that get_subscribed_events() does NOT include the imagify_settings_tools hook.
*
* The settings section is rendered directly via print_template() — no hook needed.
*/
public function testDoesNotContainSettingsToolsHook(): void {
$events = Subscriber::get_subscribed_events();

$this->assertArrayNotHasKey( 'imagify_settings_tools', $events );
}

/**
* Tests that get_subscribed_events() returns exactly one event entry.
*/
public function testReturnsExactlyOneEvent(): void {
$events = Subscriber::get_subscribed_events();

$this->assertCount( 1, $events );
}
}
Loading
Loading