Commit bb35a3557c for woocommerce

commit bb35a3557c495c186eef32f8725f2164761e18d1
Author: Chi-Hsuan Huang <chihsuan.tw@gmail.com>
Date:   Thu Apr 24 17:07:23 2025 +0800

    [Blueprint] Fix php warnings and improve error handling (#57268)

    * Enhance UseWPFunctions trait with additional filesystem methods and improved documentation

    - Added `wp_filesystem_get_contents` method for reading file contents.
    - Updated return types in existing methods for clarity.
    - Improved documentation comments for better understanding of method behaviors.

    * Refactor ExportCli to utilize UseWPFunctions for filesystem operations

    - Integrated UseWPFunctions trait into ExportCli class.
    - Replaced direct file_put_contents call with wp_filesystem_put_contents for improved filesystem handling.
    - Enhanced error handling for file saving operations.

    * Refactor ResourceStorages class to improve parameter naming and enhance clarity

    - Updated method parameters from `$resource` to `$resource_type` for better understanding.
    - Adjusted logic in `is_supported_resource` and `download` methods to reflect the new parameter naming.
    - Improved code readability by removing unnecessary comments and ensuring consistent formatting.

    * Enhance OrgPluginResourceStorage class with improved error handling in download methods

    - Updated return types in the `download` and `download_url` methods for clarity, changing from nullable types to more explicit error handling.
    - Added checks for `WP_Error` to ensure robust error management during plugin downloads.
    - Improved documentation comments to reflect the changes in return types and enhance understanding of method behaviors.

    * Improve error handling in OrgThemeResourceStorage class by checking for WP_Error before accessing download link

    - Added a check for WP_Error to return null if an error occurs, enhancing the robustness of the download link retrieval process.
    - This change ensures that the method handles errors gracefully, preventing potential issues when accessing the download link.

    * Update documentation for plugin activation and deletion methods in UsePluginHelpers trait

    - Revised return type annotations for `activate_plugin_by_slug` and `delete_plugin_by_slug` methods to provide clearer expectations.
    - Updated comments to reflect the possibility of returning `WP_Error` and `null`, enhancing understanding of error handling in these methods.

    * Refactor plugin and theme activation processes for improved clarity

    - Updated comments in `ImportActivatePlugin` to clarify the use of non-snake case property names.
    - Modified `ImportActivateTheme` to remove the unnecessary return value from `wp_switch_theme`, enhancing code readability and understanding of the method's behavior.

    * Refactor theme installation method for improved clarity

    - Updated method parameter name from `$local_plugin_path` to `$local_path` to accurately reflect its purpose.
    - Revised documentation comments to clarify that the method installs a theme from the local path, enhancing understanding of its functionality.

    * Improve error handling in ImportDeactivatePlugin by checking for WP_Error after deactivating a plugin

    - Added a check for WP_Error when attempting to deactivate a plugin, providing clearer feedback on success or failure.
    - Updated the response messages to inform users whether the deactivation was successful or if an error occurred.

    * Enhance JsonSchema class with improved error handling and filesystem integration

    - Integrated UseWPFunctions trait to utilize WordPress filesystem functions.
    - Added checks for valid schema path and improved error handling for JSON file reading.
    - Updated validation to ensure 'steps' field is an array, enhancing schema integrity.

    * Add changelog

    * Refactor theme activation process to improve clarity and remove unnecessary return value

    - Removed the return value from the `wp_switch_theme` method in `ImportActivateTheme`, enhancing code readability.
    - Added a check to confirm the theme switch was successful before logging the debug message, improving the accuracy of feedback provided to users.

    * Add tests for JsonSchema class to improve error handling coverage

    - Introduced new test methods to validate exception handling for invalid paths and unreadable files.
    - Utilized Mockery to simulate filesystem behavior, enhancing the robustness of the tests.
    - Ensured that the JsonSchema class correctly throws exceptions for various error scenarios, improving overall error handling.

    * Enhance ImportInstallPlugin with improved error handling and clarity

    - Added checks to skip installation if the plugin is already installed and to validate resource types.
    - Improved error handling during plugin installation and activation, providing clearer feedback on success or failure.
    - Updated method documentation to reflect the possibility of returning WP_Error on failure.

    * Implement error handling in ImportCli for schema file creation

    - Added try-catch block in the run method to handle exceptions when creating the blueprint from the schema file.
    - Improved user feedback by utilizing WP_CLI::error to display error messages, enhancing the robustness of the import process.

    ---------

    Co-authored-by: Vladimir Reznichenko <kalessil@gmail.com>

diff --git a/packages/php/blueprint/changelog/wooplug-3933-fix-blueprint-type-warnings-and-improve-error-handling b/packages/php/blueprint/changelog/wooplug-3933-fix-blueprint-type-warnings-and-improve-error-handling
new file mode 100644
index 0000000000..da57cd3652
--- /dev/null
+++ b/packages/php/blueprint/changelog/wooplug-3933-fix-blueprint-type-warnings-and-improve-error-handling
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix blueprint php warnings and improve error handling
diff --git a/packages/php/blueprint/src/Cli/ExportCli.php b/packages/php/blueprint/src/Cli/ExportCli.php
index cf20067df6..2f25a503ec 100644
--- a/packages/php/blueprint/src/Cli/ExportCli.php
+++ b/packages/php/blueprint/src/Cli/ExportCli.php
@@ -3,6 +3,7 @@
 namespace Automattic\WooCommerce\Blueprint\Cli;

 use Automattic\WooCommerce\Blueprint\ExportSchema;
+use Automattic\WooCommerce\Blueprint\UseWPFunctions;

 /**
  * Class ExportCli
@@ -12,6 +13,8 @@ use Automattic\WooCommerce\Blueprint\ExportSchema;
  * @package Automattic\WooCommerce\Blueprint\Cli
  */
 class ExportCli {
+	use UseWPFunctions;
+
 	/**
 	 * The path where the exported schema will be saved.
 	 *
@@ -40,11 +43,10 @@ class ExportCli {

 		$exporter = new ExportSchema();

-		$schema = $exporter->export( $args['steps'] );
+		$schema   = $exporter->export( $args['steps'] );
+		$is_saved = $this->wp_filesystem_put_contents( $this->save_to, wp_json_encode( $schema, JSON_PRETTY_PRINT ) );

-		// phpcs:ignore
-		$save = file_put_contents( $this->save_to, json_encode( $schema, JSON_PRETTY_PRINT ) );
-		if ( false === $save ) {
+		if ( false === $is_saved ) {
 			\WP_CLI::error( "Failed to save to {$this->save_to}" );
 		} else {
 			\WP_CLI::success( "Exported JSON to {$this->save_to}" );
diff --git a/packages/php/blueprint/src/Cli/ImportCli.php b/packages/php/blueprint/src/Cli/ImportCli.php
index b2e6af952f..2dc519c25e 100644
--- a/packages/php/blueprint/src/Cli/ImportCli.php
+++ b/packages/php/blueprint/src/Cli/ImportCli.php
@@ -33,8 +33,14 @@ class ImportCli {
 	 * @return void
 	 */
 	public function run( $optional_args ) {
-		$blueprint = ImportSchema::create_from_file( $this->schema_path );
-		$results   = $blueprint->import();
+		try {
+			$blueprint = ImportSchema::create_from_file( $this->schema_path );
+		} catch ( \Exception $e ) {
+			\WP_CLI::error( $e->getMessage() );
+			return;
+		}
+
+		$results = $blueprint->import();

 		$result_formatter = new CliResultFormatter( $results );
 		$is_success       = $result_formatter->is_success();
diff --git a/packages/php/blueprint/src/Importers/ImportActivatePlugin.php b/packages/php/blueprint/src/Importers/ImportActivatePlugin.php
index 75c4d5de58..e475b5cb31 100644
--- a/packages/php/blueprint/src/Importers/ImportActivatePlugin.php
+++ b/packages/php/blueprint/src/Importers/ImportActivatePlugin.php
@@ -23,8 +23,8 @@ class ImportActivatePlugin implements StepProcessor {
 	public function process( $schema ): StepProcessorResult {
 		$result = StepProcessorResult::success( ActivatePlugin::get_step_name() );

-		// phpcs:ignore
-		$plugin_path = $schema->pluginPath;
+		// Not snake case because it's a property of the schema.
+		$plugin_path = $schema->pluginPath; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase

 		$activate = $this->wp_activate_plugin( $plugin_path );

diff --git a/packages/php/blueprint/src/Importers/ImportActivateTheme.php b/packages/php/blueprint/src/Importers/ImportActivateTheme.php
index 8475782d0d..286c964a39 100644
--- a/packages/php/blueprint/src/Importers/ImportActivateTheme.php
+++ b/packages/php/blueprint/src/Importers/ImportActivateTheme.php
@@ -30,8 +30,13 @@ class ImportActivateTheme implements StepProcessor {
 		// phpcs:ignore
 		$name   = $schema->themeName;

-		$switch = $this->wp_switch_theme( $name );
-		$switch && $result->add_debug( "Switched theme to '{$name}'." );
+		$this->wp_switch_theme( $name );
+
+		$current_theme = $this->wp_get_theme()->get_stylesheet();
+
+		if ( $current_theme === $name ) {
+			$result->add_debug( "Switched theme to '$name'." );
+		}

 		return $result;
 	}
diff --git a/packages/php/blueprint/src/Importers/ImportDeactivatePlugin.php b/packages/php/blueprint/src/Importers/ImportDeactivatePlugin.php
index e66e201bda..c3e589693b 100644
--- a/packages/php/blueprint/src/Importers/ImportDeactivatePlugin.php
+++ b/packages/php/blueprint/src/Importers/ImportDeactivatePlugin.php
@@ -25,8 +25,13 @@ class ImportDeactivatePlugin implements StepProcessor {
 		// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
 		$name = $schema->pluginName;

-		$this->deactivate_plugin_by_slug( $name );
-		$result->add_info( "Deactivated {$name}." );
+		$deactivated = $this->deactivate_plugin_by_slug( $name );
+
+		if ( $this->is_wp_error( $deactivated ) ) {
+			$result->add_error( "Unable to deactivate {$name}." );
+		} else {
+			$result->add_info( "Deactivated {$name}." );
+		}

 		return $result;
 	}
diff --git a/packages/php/blueprint/src/Importers/ImportInstallPlugin.php b/packages/php/blueprint/src/Importers/ImportInstallPlugin.php
index d32802128e..4c969544d6 100644
--- a/packages/php/blueprint/src/Importers/ImportInstallPlugin.php
+++ b/packages/php/blueprint/src/Importers/ImportInstallPlugin.php
@@ -60,34 +60,46 @@ class ImportInstallPlugin implements StepProcessor {
 			return $result;
 		}

+		// If the plugin is already installed, skip the installation.
 		if ( isset( $installed_plugins[ $plugin->slug ] ) ) {
 			$result->add_info( "Skipped installing {$plugin->slug}. It is already installed." );
 			return $result;
 		}
+
+		// If the resource type is not supported, return an error.
 		if ( $this->storage->is_supported_resource( $plugin->resource ) === false ) {
 			$result->add_error( "Invalid resource type for {$plugin->slug}." );
 			return $result;
 		}

+		// Download the plugin.
 		$downloaded_path = $this->storage->download( $plugin->slug, $plugin->resource );
 		if ( ! $downloaded_path ) {
 			$result->add_error( "Unable to download {$plugin->slug} with {$plugin->resource} resource type." );
 			return $result;
 		}

+		// Install the plugin.
 		$install = $this->install( $downloaded_path );
-		$install && $result->add_info( "Installed {$plugin->slug}." );

-		if ( isset( $schema->options, $schema->options->activate ) && true === $schema->options->activate ) {
+		if ( is_wp_error( $install ) ) {
+			$result->add_error( "Failed to install {$plugin->slug}." );
+			return $result;
+		}
+
+		$result->add_info( "Installed {$plugin->slug}." );
+
+		// If the plugin should be activated, activate it.
+		$should_activate = isset( $schema->options, $schema->options->activate ) && true === $schema->options->activate;
+		if ( $should_activate ) {
 			$activate = $this->activate( $plugin->slug );

 			if ( $activate instanceof \WP_Error ) {
 				$result->add_error( "Failed to activate {$plugin->slug}." );
+				return $result;
 			}

-			if ( null === $activate ) {
-				$result->add_info( "Activated {$plugin->slug}." );
-			}
+			$result->add_info( "Activated {$plugin->slug}." );
 		}

 		return $result;
@@ -97,7 +109,7 @@ class ImportInstallPlugin implements StepProcessor {
 	 * Installs a plugin from the given local path.
 	 *
 	 * @param string $local_plugin_path Path to the local plugin file.
-	 * @return bool True on success, false on failure.
+	 * @return bool|WP_Error True on success, WP_Error on failure.
 	 */
 	protected function install( $local_plugin_path ) {
 		if ( ! class_exists( 'Plugin_Upgrader' ) ) {
diff --git a/packages/php/blueprint/src/Importers/ImportInstallTheme.php b/packages/php/blueprint/src/Importers/ImportInstallTheme.php
index 5e88593307..4ad2e1836f 100644
--- a/packages/php/blueprint/src/Importers/ImportInstallTheme.php
+++ b/packages/php/blueprint/src/Importers/ImportInstallTheme.php
@@ -120,14 +120,14 @@ class ImportInstallTheme implements StepProcessor {


 	/**
-	 * Install the theme from the local plugin path.
+	 * Install the theme from the local path.
 	 *
-	 * @param string $local_plugin_path The local path of the plugin to be installed.
+	 * @param string $local_path The local path of the theme to be installed.
 	 *
 	 * @return bool True if the installation was successful, false otherwise.
 	 */
-	protected function install( $local_plugin_path ) {
-		$unzip_result = $this->wp_unzip_file( $local_plugin_path, $this->wp_get_theme_root() );
+	protected function install( $local_path ) {
+		$unzip_result = $this->wp_unzip_file( $local_path, $this->wp_get_theme_root() );

 		if ( $this->is_wp_error( $unzip_result ) ) {
 			return false;
diff --git a/packages/php/blueprint/src/ResourceStorages.php b/packages/php/blueprint/src/ResourceStorages.php
index 98876c3212..7daa294ec7 100644
--- a/packages/php/blueprint/src/ResourceStorages.php
+++ b/packages/php/blueprint/src/ResourceStorages.php
@@ -33,32 +33,30 @@ class ResourceStorages {
 	/**
 	 * Check if the resource is supported.
 	 *
-	 * @param string $resource The resource to check.
+	 * @param string $resource_type The resource type to check.
 	 *
 	 * @return bool
 	 */
-	// phpcs:ignore
-	public function is_supported_resource( $resource ) {
-		return isset( $this->storages[ $resource ] );
+	public function is_supported_resource( $resource_type ) {
+		return isset( $this->storages[ $resource_type ] );
 	}

 	/**
 	 * Download the resource.
 	 *
 	 * @param string $slug The slug of the resource to download.
-	 * @param string $resource The resource to download.
+	 * @param string $resource_type The resource type to download.
 	 *
 	 * @return false|string
 	 */
-	// phpcs:ignore
-	public function download( $slug, $resource ) {
-		if ( ! isset( $this->storages[ $resource ] ) ) {
+	public function download( $slug, $resource_type ) {
+		if ( ! isset( $this->storages[ $resource_type ] ) ) {
 			return false;
 		}
-		$storages = $this->storages[ $resource ];
+		$storages = $this->storages[ $resource_type ];
 		foreach ( $storages as $storage ) {
-			// phpcs:ignore
-			if ( $found = $storage->download( $slug ) ) {
+			$found = $storage->download( $slug );
+			if ( $found ) {
 				return $found;
 			}
 		}
diff --git a/packages/php/blueprint/src/ResourceStorages/OrgPluginResourceStorage.php b/packages/php/blueprint/src/ResourceStorages/OrgPluginResourceStorage.php
index c36f28c8cf..7beaa09e5f 100644
--- a/packages/php/blueprint/src/ResourceStorages/OrgPluginResourceStorage.php
+++ b/packages/php/blueprint/src/ResourceStorages/OrgPluginResourceStorage.php
@@ -19,14 +19,20 @@ class OrgPluginResourceStorage implements ResourceStorage {
 	 *
 	 * @param string $slug The slug of the plugin to be downloaded.
 	 *
-	 * @return string|null The path to the downloaded plugin file, or null on failure.
+	 * @return string|false The path to the downloaded plugin file, or false on failure.
 	 */
 	public function download( $slug ): ?string {
 		$download_link = $this->get_download_link( $slug );
+
 		if ( ! $download_link ) {
 			return false;
 		}
-		return $this->download_url( $download_link );
+		$result = $this->download_url( $download_link );
+
+		if ( is_wp_error( $result ) ) {
+			return false;
+		}
+		return $result;
 	}

 	/**
@@ -34,7 +40,7 @@ class OrgPluginResourceStorage implements ResourceStorage {
 	 *
 	 * @param string $url The URL to download the file from.
 	 *
-	 * @return string|null The path to the downloaded file, or null on failure.
+	 * @return string|WP_Error The path to the downloaded file, or WP_Error on failure.
 	 */
 	protected function download_url( $url ) {
 		return $this->wp_download_url( $url );
@@ -58,6 +64,10 @@ class OrgPluginResourceStorage implements ResourceStorage {
 			)
 		);

+		if ( is_wp_error( $info ) ) {
+			return null;
+		}
+
 		if ( is_object( $info ) && isset( $info->download_link ) ) {
 			return $info->download_link;
 		}
diff --git a/packages/php/blueprint/src/ResourceStorages/OrgThemeResourceStorage.php b/packages/php/blueprint/src/ResourceStorages/OrgThemeResourceStorage.php
index 6c116e9488..0133c6a2eb 100644
--- a/packages/php/blueprint/src/ResourceStorages/OrgThemeResourceStorage.php
+++ b/packages/php/blueprint/src/ResourceStorages/OrgThemeResourceStorage.php
@@ -24,6 +24,10 @@ class OrgThemeResourceStorage extends OrgPluginResourceStorage {
 			)
 		);

+		if ( is_wp_error( $info ) ) {
+			return null;
+		}
+
 		if ( isset( $info->download_link ) ) {
 			return $info->download_link;
 		}
diff --git a/packages/php/blueprint/src/Schemas/JsonSchema.php b/packages/php/blueprint/src/Schemas/JsonSchema.php
index c38b35c979..84807389e0 100644
--- a/packages/php/blueprint/src/Schemas/JsonSchema.php
+++ b/packages/php/blueprint/src/Schemas/JsonSchema.php
@@ -2,10 +2,14 @@

 namespace Automattic\WooCommerce\Blueprint\Schemas;

+use Automattic\WooCommerce\Blueprint\UseWPFunctions;
+
 /**
  * Class JsonSchema
  */
 class JsonSchema {
+	use UseWPFunctions;
+
 	/**
 	 * The schema.
 	 *
@@ -17,11 +21,24 @@ class JsonSchema {
 	 * JsonSchema constructor.
 	 *
 	 * @param string $json_path The path to the JSON file.
+	 *
+	 * @throws \RuntimeException If the JSON file cannot be read.
 	 * @throws \InvalidArgumentException If the JSON is invalid or missing 'steps' field.
 	 */
 	public function __construct( $json_path ) {
-		// phpcs:ignore
-		$schema       = json_decode( file_get_contents( $json_path ) );
+		$real_path = realpath( $json_path );
+
+		if ( false === $real_path ) {
+			throw new \InvalidArgumentException( 'Invalid schema path' );
+		}
+
+		$contents = $this->wp_filesystem_get_contents( $real_path );
+
+		if ( false === $contents ) {
+			throw new \RuntimeException( "Failed to read the JSON file at {$real_path}." );
+		}
+
+		$schema       = json_decode( $contents );
 		$this->schema = $schema;

 		if ( ! $this->validate() ) {
@@ -69,7 +86,7 @@ class JsonSchema {
 			return false;
 		}

-		if ( ! isset( $this->schema->steps ) ) {
+		if ( ! isset( $this->schema->steps ) || ! is_array( $this->schema->steps ) ) {
 			return false;
 		}

diff --git a/packages/php/blueprint/src/UsePluginHelpers.php b/packages/php/blueprint/src/UsePluginHelpers.php
index a5f92d3bdc..9c96c02f38 100644
--- a/packages/php/blueprint/src/UsePluginHelpers.php
+++ b/packages/php/blueprint/src/UsePluginHelpers.php
@@ -13,7 +13,7 @@ trait UsePluginHelpers {
 	 *
 	 * @param string $slug The slug of the plugin to activate.
 	 *
-	 * @return bool True if the plugin was activated, false otherwise.
+	 * @return false|null|WP_Error Null on success, WP_Error on invalid file, false if not found.
 	 */
 	public function activate_plugin_by_slug( $slug ) {
 		// Get all installed plugins.
@@ -59,7 +59,7 @@ trait UsePluginHelpers {
 	 *
 	 * @param string $slug The slug of the plugin to delete.
 	 *
-	 * @return bool True if the plugin was deleted, false otherwise.
+	 * @return bool|WP_Error True if the plugin was deleted, false otherwise.
 	 */
 	public function delete_plugin_by_slug( $slug ) {
 		// Get all installed plugins.
diff --git a/packages/php/blueprint/src/UseWPFunctions.php b/packages/php/blueprint/src/UseWPFunctions.php
index a80debf9fb..27e38a1039 100644
--- a/packages/php/blueprint/src/UseWPFunctions.php
+++ b/packages/php/blueprint/src/UseWPFunctions.php
@@ -2,6 +2,9 @@

 namespace Automattic\WooCommerce\Blueprint;

+use WP_Error;
+use WP_Theme;
+
 /**
  * Trait UseWPFunctions
  */
@@ -146,7 +149,7 @@ trait UseWPFunctions {
 	 * @param string $redirect Optional. URL to redirect to after activation.
 	 * @param bool   $network_wide Optional. Whether to enable the plugin for all sites in the network.
 	 * @param bool   $silent Optional. Whether to prevent calling activation hooks.
-	 * @return \WP_Error|null WP_Error on failure, null on success.
+	 * @return WP_Error|null WP_Error on failure, null on success.
 	 */
 	public function wp_activate_plugin( $plugin, $redirect = '', $network_wide = false, $silent = false ) {
 		if ( ! function_exists( 'activate_plugin' ) ) {
@@ -160,7 +163,7 @@ trait UseWPFunctions {
 	 * Deletes plugins.
 	 *
 	 * @param array $plugins List of plugins to delete.
-	 * @return array|WP_Error Array of results or WP_Error on failure.
+	 * @return array|WP_Error|null Array of results or WP_Error on failure, null if filesystem credentials are required to proceed.
 	 */
 	public function wp_delete_plugins( $plugins ) {
 		if ( ! function_exists( 'delete_plugins' ) ) {
@@ -202,7 +205,7 @@ trait UseWPFunctions {
 		if ( ! function_exists( 'switch_theme' ) ) {
 			require_once ABSPATH . 'wp-admin/includes/theme.php';
 		}
-		return switch_theme( $name );
+		switch_theme( $name );
 	}

 	/**
@@ -216,8 +219,9 @@ trait UseWPFunctions {
 				require_once ABSPATH . 'wp-admin/includes/file.php';
 			}

-			WP_Filesystem();
-			$this->filesystem_initialized = true;
+			$initialized                  = WP_Filesystem();
+			$this->filesystem_initialized = $initialized;
+			return $initialized;
 		}

 		return true;
@@ -282,12 +286,29 @@ trait UseWPFunctions {
 	 * @param string $file_path The path to the file to write.
 	 * @param mixed  $content    The data to write to the file.
 	 *
-	 * @return mixed
+	 * @return bool True on success, false on failure.
 	 */
 	public function wp_filesystem_put_contents( $file_path, $content ) {
 		global $wp_filesystem;
-		$this->wp_init_filesystem();
+		if ( ! $this->wp_init_filesystem() ) {
+			return false;
+		}

 		return $wp_filesystem->put_contents( $file_path, $content );
 	}
+
+	/**
+	 * Alias for WP_Filesystem::get_contents().
+	 *
+	 * @param string $file_path The path to the file to read.
+	 * @return string|false The contents of the file, or false on failure.
+	 */
+	public function wp_filesystem_get_contents( $file_path ) {
+		global $wp_filesystem;
+		if ( ! $this->wp_init_filesystem() ) {
+			return false;
+		}
+
+		return $wp_filesystem->get_contents( $file_path );
+	}
 }
diff --git a/packages/php/blueprint/tests/Unit/Importers/ImportActivateThemeTest.php b/packages/php/blueprint/tests/Unit/Importers/ImportActivateThemeTest.php
index 839b0f0060..ec8ed4f2b2 100644
--- a/packages/php/blueprint/tests/Unit/Importers/ImportActivateThemeTest.php
+++ b/packages/php/blueprint/tests/Unit/Importers/ImportActivateThemeTest.php
@@ -2,11 +2,13 @@

 namespace Automattic\WooCommerce\Blueprint\Tests\Unit\Importers;

+use PHPUnit\Framework\TestCase;
+use Mockery;
+
 use Automattic\WooCommerce\Blueprint\Importers\ImportActivateTheme;
 use Automattic\WooCommerce\Blueprint\StepProcessorResult;
 use Automattic\WooCommerce\Blueprint\Steps\ActivateTheme;
-use Mockery;
-use PHPUnit\Framework\TestCase;
+

 /**
  * Test the ImportActivateTheme class.
@@ -43,8 +45,12 @@ class ImportActivateThemeTest extends TestCase {

 		// Mock the wp_switch_theme method.
 		$import_activate_theme->shouldReceive( 'wp_switch_theme' )
-			->with( $theme_name )
-			->andReturn( true );
+			->with( $theme_name );
+
+		// Mock the wp_get_theme method to return a mock object with a get_stylesheet method.
+		$theme_mock = Mockery::mock();
+		$theme_mock->shouldReceive( 'get_stylesheet' )->andReturn( $theme_name );
+		$import_activate_theme->shouldReceive( 'wp_get_theme' )->andReturn( $theme_mock );

 		// Execute the process method.
 		$result = $import_activate_theme->process( $schema );
@@ -81,8 +87,12 @@ class ImportActivateThemeTest extends TestCase {

 		// Mock the wp_switch_theme method.
 		$import_activate_theme->shouldReceive( 'wp_switch_theme' )
-			->with( $theme_name )
-			->andReturn( false );
+			->with( $theme_name );
+
+		// Mock the wp_get_theme method to return a mock object with a get_stylesheet method.
+		$theme_mock = Mockery::mock();
+		$theme_mock->shouldReceive( 'get_stylesheet' )->andReturn( 'different-theme' );
+		$import_activate_theme->shouldReceive( 'wp_get_theme' )->andReturn( $theme_mock );

 		// Execute the process method.
 		$result = $import_activate_theme->process( $schema );
diff --git a/packages/php/blueprint/tests/Unit/Schemas/JsonSchemaTest.php b/packages/php/blueprint/tests/Unit/Schemas/JsonSchemaTest.php
index 7e8ca9a7fc..792b3f4e03 100644
--- a/packages/php/blueprint/tests/Unit/Schemas/JsonSchemaTest.php
+++ b/packages/php/blueprint/tests/Unit/Schemas/JsonSchemaTest.php
@@ -2,6 +2,7 @@

 namespace Automattic\WooCommerce\Blueprint\Tests\Unit\Schemas;

+use Mockery;
 use Automattic\WooCommerce\Blueprint\Schemas\JsonSchema;
 use Automattic\WooCommerce\Blueprint\Tests\TestCase;

@@ -45,4 +46,30 @@ class JsonSchemaTest extends TestCase {
 		$this->expectException( \InvalidArgumentException::class );
 		new JsonSchema( $this->get_fixture_path( 'invalid-json.json' ) );
 	}
+
+	/**
+	 * Test that it throws an invalid argument exception with an invalid path.
+	 *
+	 * @return void
+	 */
+	public function test_it_throws_invalid_argument_exception_with_invalid_path() {
+		$this->expectException( \InvalidArgumentException::class );
+		new JsonSchema( $this->get_fixture_path( 'invalid-path.json' ) );
+	}
+
+	/**
+	 * Test that it throws a runtime exception when the file is not readable.
+	 *
+	 * @return void
+	 */
+	public function test_it_throws_runtime_exception_when_file_is_not_readable() {
+		$this->expectException( \RuntimeException::class );
+
+		$mock = Mockery::mock( JsonSchema::class )->makePartial();
+		$mock->shouldReceive( 'wp_filesystem_get_contents' )
+			->once()
+			->andReturn( false );
+
+		$mock->__construct( $this->get_fixture_path( 'invalid-json.json' ) );
+	}
 }