From b496d2946e531bbc4bae2f16d4c895d9e780feef Mon Sep 17 00:00:00 2001 From: yusufhay <32195740+yusufhay@users.noreply.github.com> Date: Thu, 4 Jun 2026 01:02:37 +0530 Subject: [PATCH 1/3] Handle image import setup failures --- includes/Abilities/Image/Import_Base64_Image.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/includes/Abilities/Image/Import_Base64_Image.php b/includes/Abilities/Image/Import_Base64_Image.php index 497106195..c03c8ab71 100644 --- a/includes/Abilities/Image/Import_Base64_Image.php +++ b/includes/Abilities/Image/Import_Base64_Image.php @@ -265,6 +265,13 @@ protected function import_image( string $data, array $args = array() ) { // Create a temporary file. $temp_file = wp_tempnam( 'ai-image' ); + if ( ! $temp_file ) { + return new WP_Error( + 'temp_file_failed', + esc_html__( 'Failed to create a temporary image file.', 'ai' ) + ); + } + // Write the decoded data to the temporary file. $bytes_written = file_put_contents( $temp_file, $decoded_data ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_file_put_contents @@ -279,6 +286,14 @@ protected function import_image( string $data, array $args = array() ) { // Determine file extension from MIME type. $extension = wp_get_default_extension_for_mime_type( $args['mime_type'] ); + if ( ! $extension ) { + wp_delete_file( $temp_file ); + return new WP_Error( + 'invalid_mime_type', + esc_html__( 'Could not determine the image file extension from the MIME type.', 'ai' ) + ); + } + /** * Filters the base filename (without extension) used when importing an * AI-generated image. From 1a453166e4bc2c240c0ba93bf4b7488db8c1235d Mon Sep 17 00:00:00 2001 From: yusufhay <32195740+yusufhay@users.noreply.github.com> Date: Thu, 4 Jun 2026 01:37:05 +0530 Subject: [PATCH 2/3] Make image import setup failures testable --- .../Abilities/Image/Import_Base64_Image.php | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/includes/Abilities/Image/Import_Base64_Image.php b/includes/Abilities/Image/Import_Base64_Image.php index c03c8ab71..a9a9bd666 100644 --- a/includes/Abilities/Image/Import_Base64_Image.php +++ b/includes/Abilities/Image/Import_Base64_Image.php @@ -232,6 +232,29 @@ protected function meta(): array { ); } + /** + * Creates a temporary file path for the imported image. + * + * @since x.x.x + * + * @return string|false Temporary file path, or false on failure. + */ + protected function create_temp_file() { + return wp_tempnam( 'ai-image' ); + } + + /** + * Gets the default file extension for a MIME type. + * + * @since x.x.x + * + * @param string $mime_type The MIME type. + * @return string|false File extension, or false when unknown. + */ + protected function get_default_extension_for_mime_type( string $mime_type ) { + return wp_get_default_extension_for_mime_type( $mime_type ); + } + /** * Imports an image from a base64 encoded string into the media library. * @@ -263,7 +286,7 @@ protected function import_image( string $data, array $args = array() ) { } // Create a temporary file. - $temp_file = wp_tempnam( 'ai-image' ); + $temp_file = $this->create_temp_file(); if ( ! $temp_file ) { return new WP_Error( @@ -284,7 +307,7 @@ protected function import_image( string $data, array $args = array() ) { } // Determine file extension from MIME type. - $extension = wp_get_default_extension_for_mime_type( $args['mime_type'] ); + $extension = $this->get_default_extension_for_mime_type( $args['mime_type'] ); if ( ! $extension ) { wp_delete_file( $temp_file ); From d078baa888300c9071e899a36e385358bb0d98be Mon Sep 17 00:00:00 2001 From: yusufhay <32195740+yusufhay@users.noreply.github.com> Date: Thu, 4 Jun 2026 01:37:07 +0530 Subject: [PATCH 3/3] Cover image import setup failure handling --- .../Includes/Abilities/Image_ImportTest.php | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/tests/Integration/Includes/Abilities/Image_ImportTest.php b/tests/Integration/Includes/Abilities/Image_ImportTest.php index 070e120a0..b08a19197 100644 --- a/tests/Integration/Includes/Abilities/Image_ImportTest.php +++ b/tests/Integration/Includes/Abilities/Image_ImportTest.php @@ -45,6 +45,84 @@ public function register(): void { } } +/** + * Testable image import ability. + * + * @since x.x.x + */ +class Testable_Image_Import extends Import { + /** + * Temporary file path to return. + * + * @var string|false|null + */ + private $temp_file = null; + + /** + * Extension to return. + * + * @var string|false|null + */ + private $extension = null; + + /** + * Sets the temporary file path to return. + * + * @since x.x.x + * + * @param string|false $temp_file Temporary file path, or false. + */ + public function set_temp_file( $temp_file ): void { + $this->temp_file = $temp_file; + } + + /** + * Sets the extension to return. + * + * @since x.x.x + * + * @param string|false $extension Extension, or false. + */ + public function set_extension( $extension ): void { + $this->extension = $extension; + } + + /** + * Calls the protected image import method. + * + * @since x.x.x + * + * @param string $data Base64 image data. + * @param array $args Import arguments. + * @return array|\WP_Error Attachment data, or error. + */ + public function call_import_image( string $data, array $args = array() ) { + return $this->import_image( $data, $args ); + } + + /** + * {@inheritDoc} + */ + protected function create_temp_file() { + if ( null !== $this->temp_file ) { + return $this->temp_file; + } + + return parent::create_temp_file(); + } + + /** + * {@inheritDoc} + */ + protected function get_default_extension_for_mime_type( string $mime_type ) { + if ( null !== $this->extension ) { + return $this->extension; + } + + return parent::get_default_extension_for_mime_type( $mime_type ); + } +} + /** * Image_Import Ability test case. * @@ -225,6 +303,66 @@ public function test_execute_callback_with_valid_data() { $this->assertNotEmpty( $result['image']['url'], 'Image URL should not be empty' ); } + /** + * Test that import_image() returns an error when a temp file cannot be created. + * + * @since x.x.x + */ + public function test_import_image_returns_error_when_temp_file_creation_fails() { + $ability = new Testable_Image_Import( + 'ai/image-import', + array( + 'label' => $this->experiment->get_label(), + 'description' => $this->experiment->get_description(), + ) + ); + $ability->set_temp_file( false ); + + $result = $ability->call_import_image( + $this->valid_base64_image, + array( + 'filename' => 'test-image', + 'title' => 'Test Image', + 'description' => '', + 'alt_text' => '', + 'mime_type' => 'image/png', + ) + ); + + $this->assertInstanceOf( WP_Error::class, $result, 'Result should be WP_Error' ); + $this->assertEquals( 'temp_file_failed', $result->get_error_code(), 'Error code should be temp_file_failed' ); + } + + /** + * Test that import_image() returns an error when MIME extension cannot be determined. + * + * @since x.x.x + */ + public function test_import_image_returns_error_when_mime_extension_is_unknown() { + $ability = new Testable_Image_Import( + 'ai/image-import', + array( + 'label' => $this->experiment->get_label(), + 'description' => $this->experiment->get_description(), + ) + ); + $ability->set_extension( false ); + + $result = $ability->call_import_image( + $this->valid_base64_image, + array( + 'filename' => 'test-image', + 'title' => 'Test Image', + 'description' => '', + 'alt_text' => '', + 'mime_type' => 'image/png', + ) + ); + + $this->assertInstanceOf( WP_Error::class, $result, 'Result should be WP_Error' ); + $this->assertEquals( 'invalid_mime_type', $result->get_error_code(), 'Error code should be invalid_mime_type' ); + } + /** * Test that execute_callback() handles custom filename, title, and description. *